<!DOCTYPE html><html lang="zh-CN" data-theme="light"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"><title>零基础学-Java-03 | 无人深空</title><meta name="keywords" content="Java"><meta name="author" content="zcm"><meta name="copyright" content="zcm"><meta name="format-detection" content="telephone=no"><meta name="theme-color" content="#ffffff"><meta name="description" content="网络编程 在开发中尽量不要使用 0~1024 端口（2000 以内都别用）  客户端和服务端都有端口，客户端端口随机分配，服务器端口需指定  InetAddress 类：可序列化，实现了 Serializable  getLocalHost：获取本机 InetAddress 对象 getByName：根据指定主机名 &#x2F; 域名获取 ip 地址对象 getHostName：获取 InetAd">
<meta property="og:type" content="article">
<meta property="og:title" content="零基础学-Java-03">
<meta property="og:url" content="https://gitee.com/zcmmmm/zcmmmm/2022/09/23/%E9%9B%B6%E5%9F%BA%E7%A1%80%E5%AD%A6-Java-03/index.html">
<meta property="og:site_name" content="无人深空">
<meta property="og:description" content="网络编程 在开发中尽量不要使用 0~1024 端口（2000 以内都别用）  客户端和服务端都有端口，客户端端口随机分配，服务器端口需指定  InetAddress 类：可序列化，实现了 Serializable  getLocalHost：获取本机 InetAddress 对象 getByName：根据指定主机名 &#x2F; 域名获取 ip 地址对象 getHostName：获取 InetAd">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/%E9%9B%B6%E5%9F%BA%E7%A1%80%E5%AD%A6Java/Cover.jpg">
<meta property="article:published_time" content="2022-09-23T01:23:37.000Z">
<meta property="article:modified_time" content="2022-09-23T02:49:00.331Z">
<meta property="article:author" content="zcm">
<meta property="article:tag" content="Java">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/%E9%9B%B6%E5%9F%BA%E7%A1%80%E5%AD%A6Java/Cover.jpg"><link rel="shortcut icon" href="/img/favicon.png"><link rel="canonical" href="https://gitee.com/zcmmmm/zcmmmm/2022/09/23/%E9%9B%B6%E5%9F%BA%E7%A1%80%E5%AD%A6-Java-03/"><link rel="preconnect" href="//cdn.jsdelivr.net"/><link rel="preconnect" href="//busuanzi.ibruce.info"/><link rel="stylesheet" href="/css/index.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free/css/all.min.css" media="print" onload="this.media='all'"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/node-snackbar/dist/snackbar.min.css" media="print" onload="this.media='all'"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fancyapps/ui/dist/fancybox.min.css" media="print" onload="this.media='all'"><script>const GLOBAL_CONFIG = { 
  root: '/',
  algolia: undefined,
  localSearch: {"path":"/search.xml","preload":false,"languages":{"hits_empty":"找不到您查询的内容：${query}"}},
  translate: undefined,
  noticeOutdate: undefined,
  highlight: {"plugin":"highlighjs","highlightCopy":true,"highlightLang":true,"highlightHeightLimit":200},
  copy: {
    success: '复制成功',
    error: '复制错误',
    noSupport: '浏览器不支持'
  },
  relativeDate: {
    homepage: false,
    post: false
  },
  runtime: '天',
  date_suffix: {
    just: '刚刚',
    min: '分钟前',
    hour: '小时前',
    day: '天前',
    month: '个月前'
  },
  copyright: undefined,
  lightbox: 'fancybox',
  Snackbar: {"chs_to_cht":"你已切换为繁体","cht_to_chs":"你已切换为简体","day_to_night":"你已切换为深色模式","night_to_day":"你已切换为浅色模式","bgLight":"#49b1f5","bgDark":"#1f1f1f","position":"bottom-left"},
  source: {
    justifiedGallery: {
      js: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery/dist/fjGallery.min.js',
      css: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery/dist/fjGallery.min.css'
    }
  },
  isPhotoFigcaption: false,
  islazyload: false,
  isAnchor: false
}</script><script id="config-diff">var GLOBAL_CONFIG_SITE = {
  title: '零基础学-Java-03',
  isPost: true,
  isHome: false,
  isHighlightShrink: false,
  isToc: true,
  postUpdate: '2022-09-23 10:49:00'
}</script><noscript><style type="text/css">
  #nav {
    opacity: 1
  }
  .justified-gallery img {
    opacity: 1
  }

  #recent-posts time,
  #post-meta time {
    display: inline !important
  }
</style></noscript><script>(win=>{
    win.saveToLocal = {
      set: function setWithExpiry(key, value, ttl) {
        if (ttl === 0) return
        const now = new Date()
        const expiryDay = ttl * 86400000
        const item = {
          value: value,
          expiry: now.getTime() + expiryDay,
        }
        localStorage.setItem(key, JSON.stringify(item))
      },

      get: function getWithExpiry(key) {
        const itemStr = localStorage.getItem(key)

        if (!itemStr) {
          return undefined
        }
        const item = JSON.parse(itemStr)
        const now = new Date()

        if (now.getTime() > item.expiry) {
          localStorage.removeItem(key)
          return undefined
        }
        return item.value
      }
    }
  
    win.getScript = url => new Promise((resolve, reject) => {
      const script = document.createElement('script')
      script.src = url
      script.async = true
      script.onerror = reject
      script.onload = script.onreadystatechange = function() {
        const loadState = this.readyState
        if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
        script.onload = script.onreadystatechange = null
        resolve()
      }
      document.head.appendChild(script)
    })
  
      win.activateDarkMode = function () {
        document.documentElement.setAttribute('data-theme', 'dark')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#0d0d0d')
        }
      }
      win.activateLightMode = function () {
        document.documentElement.setAttribute('data-theme', 'light')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#ffffff')
        }
      }
      const t = saveToLocal.get('theme')
    
          if (t === 'dark') activateDarkMode()
          else if (t === 'light') activateLightMode()
        
      const asideStatus = saveToLocal.get('aside-status')
      if (asideStatus !== undefined) {
        if (asideStatus === 'hide') {
          document.documentElement.classList.add('hide-aside')
        } else {
          document.documentElement.classList.remove('hide-aside')
        }
      }
    
    const detectApple = () => {
      if(/iPad|iPhone|iPod|Macintosh/.test(navigator.userAgent)){
        document.documentElement.classList.add('apple')
      }
    }
    detectApple()
    })(window)</script><link rel="stylesheet" href="/css/categories.css?v1"><link rel="stylesheet" href="/css/navigation.css?v1"><link rel="stylesheet" href="/css/custom.css?v1"><meta name="generator" content="Hexo 6.3.0"></head><body><div id="sidebar"><div id="menu-mask"></div><div id="sidebar-menus"><div class="avatar-img is-center"><img src="https://s2.loli.net/2022/09/22/36LN4kcjYOd9HT8.jpg" onerror="onerror=null;src='/img/friend_404.gif'" alt="avatar"/></div><div class="sidebar-site-data site-data is-center"><a href="/archives/"><div class="headline">文章</div><div class="length-num">43</div></a><a href="/tags/"><div class="headline">标签</div><div class="length-num">25</div></a><a href="/categories/"><div class="headline">分类</div><div class="length-num">11</div></a></div><hr/><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw fas fa-home"></i><span> 首页</span></a></div><div class="menus_item"><a class="site-page" href="/archives/"><i class="fa-fw fas fa-archive"></i><span> 时间轴</span></a></div><div class="menus_item"><a class="site-page" href="/tags/"><i class="fa-fw fas fa-tags"></i><span> 标签</span></a></div><div class="menus_item"><a class="site-page" href="/categories/"><i class="fa-fw fas fa-folder-open"></i><span> 分类</span></a></div></div></div></div><div class="post" id="body-wrap"><header class="not-top-img" id="page-header"><nav id="nav"><span id="blog_name"><a id="site-name" href="/">无人深空</a></span><div id="menus"></div><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw fas fa-home"></i><span> 首页</span></a></div><div class="menus_item"><a class="site-page" href="/archives/"><i class="fa-fw fas fa-archive"></i><span> 时间轴</span></a></div><div class="menus_item"><a class="site-page" href="/tags/"><i class="fa-fw fas fa-tags"></i><span> 标签</span></a></div><div class="menus_item"><a class="site-page" href="/categories/"><i class="fa-fw fas fa-folder-open"></i><span> 分类</span></a></div></div><div id="nav-right"><div id="search-button"><a class="site-page social-icon search"><i class="fas fa-search fa-fw"></i></a></div><div id="toggle-menu"><a class="site-page"><i class="fas fa-bars fa-fw"></i></a></div></div></nav></header><main class="layout" id="content-inner"><div id="post"><div id="post-info"><h1 class="post-title">零基础学-Java-03</h1><div id="post-meta"><div class="meta-firstline"><span class="post-meta-date"><i class="far fa-calendar-alt fa-fw post-meta-icon"></i><span class="post-meta-label">发表于</span><time class="post-meta-date-created" datetime="2022-09-23T01:23:37.000Z" title="发表于 2022-09-23 09:23:37">2022-09-23</time><span class="post-meta-separator">|</span><i class="fas fa-history fa-fw post-meta-icon"></i><span class="post-meta-label">更新于</span><time class="post-meta-date-updated" datetime="2022-09-23T02:49:00.331Z" title="更新于 2022-09-23 10:49:00">2022-09-23</time></span><span class="post-meta-categories"><span class="post-meta-separator">|</span><i class="fas fa-inbox fa-fw post-meta-icon"></i><a class="post-meta-categories" href="/categories/Java/">Java</a></span></div><div class="meta-secondline"><span class="post-meta-separator">|</span><span class="post-meta-wordcount"><i class="far fa-file-word fa-fw post-meta-icon"></i><span class="post-meta-label">字数总计:</span><span class="word-count">24.9k</span><span class="post-meta-separator">|</span><i class="far fa-clock fa-fw post-meta-icon"></i><span class="post-meta-label">阅读时长:</span><span>104分钟</span></span><span class="post-meta-separator">|</span><span class="post-meta-pv-cv" id="" data-flag-title="零基础学-Java-03"><i class="far fa-eye fa-fw post-meta-icon"></i><span class="post-meta-label">阅读量:</span><span id="busuanzi_value_page_pv"><i class="fa-solid fa-spinner fa-spin"></i></span></span></div></div></div><article class="post-content" id="article-container"><h3 id="网络编程"><a href="#网络编程" class="headerlink" title="网络编程"></a>网络编程</h3><ul>
<li><p>在开发中尽量不要使用 0~1024 端口（2000 以内都别用）</p>
</li>
<li><p>客户端和服务端都有端口，客户端端口随机分配，服务器端口需指定</p>
</li>
<li><p><code>InetAddress</code> 类：可序列化，实现了 <code>Serializable</code></p>
<ul>
<li><code>getLocalHost</code>：获取本机 <code>InetAddress</code> 对象</li>
<li><code>getByName</code>：根据指定主机名 &#x2F; 域名获取 <code>ip</code> 地址对象</li>
<li><code>getHostName</code>：获取 <code>InetAddress</code> 对象的主机名</li>
<li><code>getHostAddress</code>：获取 <code>InetAddress</code> 对象的地址</li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 1.获取本机 InetAddress 对象</span></span><br><span class="line"><span class="type">InetAddress</span> <span class="variable">localHost</span> <span class="operator">=</span> InetAddress.getLocalHost();</span><br><span class="line">System.out.println(localHost);      <span class="comment">// LAPTOP-51JTTFR5/192.168.0.110</span></span><br><span class="line"><span class="comment">// 2.根据指定主机名 ip 地址对象</span></span><br><span class="line"><span class="type">InetAddress</span> <span class="variable">host</span> <span class="operator">=</span> InetAddress.getByName(<span class="string">&quot;LAPTOP-51JTTFR5&quot;</span>);</span><br><span class="line">System.out.println(host);           <span class="comment">// LAPTOP-51JTTFR5/192.168.0.110</span></span><br><span class="line"><span class="comment">// 3.根据指定域名获取 ip 地址对象</span></span><br><span class="line"><span class="type">InetAddress</span> <span class="variable">baidu</span> <span class="operator">=</span> InetAddress.getByName(<span class="string">&quot;www.baidu.com&quot;</span>);</span><br><span class="line">System.out.println(baidu);          <span class="comment">// www.baidu.com/14.215.177.39</span></span><br><span class="line"><span class="comment">// 4.获取 InetAddress 对象的主机名</span></span><br><span class="line"><span class="type">String</span> <span class="variable">hostName</span> <span class="operator">=</span> baidu.getHostName();</span><br><span class="line">System.out.println(hostName);       <span class="comment">// www.baidu.com</span></span><br><span class="line"><span class="comment">// 5.获取 InetAddress 对象的地址</span></span><br><span class="line"><span class="type">String</span> <span class="variable">hostAddress</span> <span class="operator">=</span> localHost.getHostAddress();</span><br><span class="line">System.out.println(hostAddress);    <span class="comment">// 192.168.0.110</span></span><br></pre></td></tr></table></figure>
</li>
<li><p>Socket（套接字）</p>
<ul>
<li>Socket &#x3D; IP 地址 + 端口</li>
<li>网络通信即 Socket 间的通信</li>
<li>使用 Socket 可以把网络通信当作流处理<ul>
<li><code>socket.getInputStream()</code></li>
<li><code>socket.getOutputStream()</code></li>
</ul>
</li>
<li>Socket 有两种编程方式：TCP、UDP</li>
</ul>
</li>
<li><p>TCP Socket 编程</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// Server</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">    <span class="comment">// 1.在本机的 9999 端口监听，等待连接</span></span><br><span class="line">    <span class="comment">//   要求 9999 端口未被使用</span></span><br><span class="line">    <span class="type">ServerSocket</span> <span class="variable">serverSocket</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ServerSocket</span>(<span class="number">9999</span>);</span><br><span class="line">    System.out.println(<span class="string">&quot;服务器监听 9999 端口，等待连接...&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 2.当没有客户端连接该端口时，程序会阻塞，等待连接</span></span><br><span class="line">    <span class="comment">//   如果连接成功则返回 Socket 对象，程序继续运行</span></span><br><span class="line">    <span class="comment">//   ServerSocket 可通过 accept() 打开多个 socket</span></span><br><span class="line">    <span class="type">Socket</span> <span class="variable">socket</span> <span class="operator">=</span> serverSocket.accept();</span><br><span class="line">    System.out.println(<span class="string">&quot;服务器端 socket=&quot;</span> + socket.getClass());</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 3.（字节流）通过 socket.getInputStream() 读取客户端发送的数据</span></span><br><span class="line">    <span class="comment">//        InputStream inputStream = socket.getInputStream();</span></span><br><span class="line">    <span class="comment">//        byte[] buf = new byte[1024];</span></span><br><span class="line">    <span class="comment">//        int readLen = 0;</span></span><br><span class="line">    <span class="comment">//        while((readLen = inputStream.read(buf)) != -1) &#123;</span></span><br><span class="line">    <span class="comment">//            System.out.println(new String(buf, 0, readLen));</span></span><br><span class="line">    <span class="comment">//        &#125;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 3.（字符流）通过 socket.getInputStream() 读取客户端发送的数据</span></span><br><span class="line">    <span class="comment">//    对方用 bufferedWriter.newLine() 设置换行结束标记，必须用 readLine() 接收信息</span></span><br><span class="line">    <span class="type">InputStream</span> <span class="variable">inputStream</span> <span class="operator">=</span> socket.getInputStream();</span><br><span class="line">    <span class="type">BufferedReader</span> <span class="variable">bufferedReader</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BufferedReader</span>(<span class="keyword">new</span> <span class="title class_">InputStreamReader</span>(inputStream));</span><br><span class="line">    <span class="type">String</span> <span class="variable">line</span> <span class="operator">=</span> <span class="string">&quot;&quot;</span>;</span><br><span class="line">    <span class="keyword">while</span>((line = bufferedReader.readLine()) != <span class="literal">null</span>) &#123;</span><br><span class="line">        System.out.println(line);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 4.（字节流）回发消息到客户端</span></span><br><span class="line">    <span class="comment">//   socket.shutdownOutput() 设置结束标记</span></span><br><span class="line">    <span class="comment">//        OutputStream outputStream = socket.getOutputStream();</span></span><br><span class="line">    <span class="comment">//        outputStream.write(&quot;字节流 Server: hello client&quot;.getBytes());</span></span><br><span class="line">    <span class="comment">//        socket.shutdownOutput();</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 4.（字符流）回发消息到客户端</span></span><br><span class="line">    <span class="comment">//   socket.shutdownOutput() 设置结束标记(推荐)</span></span><br><span class="line">    <span class="comment">//   bufferedWriter.newLine() 设置换行结束标记，要求对方用 readLine() 接收信息</span></span><br><span class="line">    <span class="type">OutputStream</span> <span class="variable">outputStream</span> <span class="operator">=</span> socket.getOutputStream();</span><br><span class="line">    <span class="type">BufferedWriter</span> <span class="variable">bufferedWriter</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BufferedWriter</span>(<span class="keyword">new</span> <span class="title class_">OutputStreamWriter</span>(outputStream));</span><br><span class="line">    bufferedWriter.write(<span class="string">&quot;字符流 Server: hello client&quot;</span>);</span><br><span class="line">    bufferedWriter.newLine();   <span class="comment">// 换行表结束</span></span><br><span class="line">    bufferedWriter.flush();     <span class="comment">// 需手动刷新</span></span><br><span class="line">    <span class="comment">//        socket.shutdownOutput();    // 设置结束标值</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 5.（字节流）关闭流和 socket、serverSocket</span></span><br><span class="line">    <span class="comment">//        inputStream.close();</span></span><br><span class="line">    <span class="comment">//        outputStream.close();</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 5.（字符流）关闭流和 socket、serverSocket</span></span><br><span class="line">    bufferedReader.close();</span><br><span class="line">    bufferedWriter.close();</span><br><span class="line"></span><br><span class="line">    socket.close();</span><br><span class="line">    serverSocket.close();</span><br><span class="line">    System.out.println(<span class="string">&quot;服务器已关闭...&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// Client</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">    <span class="comment">// 1.连接服务器(IP、端口)</span></span><br><span class="line">    <span class="comment">//   连接 localhost 的 9999 端口，连接成功则返回 socket</span></span><br><span class="line">    <span class="type">Socket</span> <span class="variable">socket</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Socket</span>(InetAddress.getLocalHost(), <span class="number">9999</span>);</span><br><span class="line">    System.out.println(<span class="string">&quot;客户端 socket=&quot;</span> + socket.getClass());</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 2.（字节流）通过 socket.getOutputStream() 发送信息</span></span><br><span class="line">    <span class="comment">//   将要发送的数据写到数据通道</span></span><br><span class="line">    <span class="comment">//   socket.shutdownOutput() 设置结束标记</span></span><br><span class="line">    <span class="comment">//        OutputStream outputStream = socket.getOutputStream();</span></span><br><span class="line">    <span class="comment">//        outputStream.write(&quot;字节流 Client: hello server&quot;.getBytes());</span></span><br><span class="line">    <span class="comment">//        socket.shutdownOutput();</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 2.（字符流）通过 socket.getOutputStream() 发送信息</span></span><br><span class="line">    <span class="comment">//   将要发送的数据写到数据通道</span></span><br><span class="line">    <span class="comment">//   socket.shutdownOutput() 设置结束标记(推荐) 或</span></span><br><span class="line">    <span class="comment">//   bufferedWriter.newLine() 设置换行结束标记，要求对方用 readLine() 接收信息</span></span><br><span class="line">    <span class="type">OutputStream</span> <span class="variable">outputStream</span> <span class="operator">=</span> socket.getOutputStream();</span><br><span class="line">    <span class="type">BufferedWriter</span> <span class="variable">bufferedWriter</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BufferedWriter</span>(<span class="keyword">new</span> <span class="title class_">OutputStreamWriter</span>(outputStream));</span><br><span class="line">    bufferedWriter.write(<span class="string">&quot;字符流 Client: hello server&quot;</span>);</span><br><span class="line">    <span class="comment">//        bufferedWriter.newLine();   // 换行表结束</span></span><br><span class="line">    bufferedWriter.flush();     <span class="comment">// 需手动刷新</span></span><br><span class="line">    socket.shutdownOutput();    <span class="comment">// 设置结束标值</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 3.（字节流）接受服务端回发的信息</span></span><br><span class="line">    <span class="comment">//        InputStream inputStream = socket.getInputStream();</span></span><br><span class="line">    <span class="comment">//        byte[] buf = new byte[1024];</span></span><br><span class="line">    <span class="comment">//        int readLen = 0;</span></span><br><span class="line">    <span class="comment">//        while((readLen = inputStream.read(buf)) != -1) &#123;</span></span><br><span class="line">    <span class="comment">//            System.out.println(new String(buf, 0, readLen));</span></span><br><span class="line">    <span class="comment">//        &#125;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 3.（字符流）接受服务端回发的信息</span></span><br><span class="line">    <span class="type">InputStream</span> <span class="variable">inputStream</span> <span class="operator">=</span> socket.getInputStream();</span><br><span class="line">    <span class="type">BufferedReader</span> <span class="variable">bufferedReader</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BufferedReader</span>(<span class="keyword">new</span> <span class="title class_">InputStreamReader</span>(inputStream));</span><br><span class="line">    <span class="type">String</span> <span class="variable">line</span> <span class="operator">=</span> <span class="string">&quot;&quot;</span>;</span><br><span class="line">    <span class="keyword">while</span>((line = bufferedReader.readLine()) != <span class="literal">null</span>) &#123;</span><br><span class="line">        System.out.println(line);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 4.（字节流）关闭流对象和 socket，必须关闭</span></span><br><span class="line">    <span class="comment">//        outputStream.close();</span></span><br><span class="line">    <span class="comment">//        inputStream.close();</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 4.（字符流）关闭流对象和 socket，必须关闭</span></span><br><span class="line">    bufferedWriter.close();</span><br><span class="line">    bufferedReader.close();</span><br><span class="line"></span><br><span class="line">    socket.close();</span><br><span class="line">    System.out.println(<span class="string">&quot;客户端退出...&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>网络传文件：</p>
<ul>
<li><a target="_blank" rel="noopener" href="https://www.bilibili.com/video/BV1fh411y7R8?p=672">客户端上传到服务端</a><ul>
<li>发送方读取文件保存为字节数组</li>
<li>发送方发送文件</li>
<li>接收方接收文件</li>
<li>接收方将文件保存到磁盘</li>
<li>接收方发送<code>确认收到</code>消息</li>
<li>发送方接收<code>确认收到</code>消息</li>
</ul>
</li>
<li><a target="_blank" rel="noopener" href="https://www.bilibili.com/video/BV1fh411y7R8?p=682">客户端从服务端下载</a>（大致步骤同上）<ul>
<li>客户端发送请求文件名</li>
<li>服务器端查看有无该文件，有则读取文件保存为字节数组</li>
<li>服务器端发送文件</li>
<li>客户端接收文件</li>
<li>客户端将文件保存到磁盘</li>
<li>客户端发送<code>确认收到</code>消息</li>
<li>服务器端接收<code>确认收到</code>消息</li>
</ul>
</li>
</ul>
</li>
<li><p><code>netstat</code> 指令</p>
<ul>
<li><code>netstat -an</code> 查看当前主机网络情况，包括<code>端口监听</code>情况和<code>网络连接</code>情况</li>
<li><code>netstat -an | more</code> 分页查看</li>
</ul>
</li>
<li><p>UDP Socket 编程</p>
<ul>
<li>一个数据包最大 64K，不适合传大量数据</li>
<li>无明确服务端和客户端，演变为发送端和接收端（两者角色常常互换）</li>
<li>接收和发送数据通过 <code>DatagramSocket</code> 对象完成</li>
<li>将数据封装到 <code>DatagramPacket</code> 对象再传输（需 装包、拆包）</li>
<li>收发两端均可指定端口接收</li>
<li>基本流程（核心类 <code>DatagramSocket</code>、<code>DatagramPacket</code>）<ul>
<li>建立发送端、接收端</li>
<li>建立数据包</li>
<li>调用 <code>DatagramSocke</code> 的收发方法</li>
<li>关闭 <code>DatagramSocke</code></li>
</ul>
</li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 接收端</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">    <span class="comment">// 1.创建 DatagramSocket 对象，端口为 9999</span></span><br><span class="line">    <span class="type">DatagramSocket</span> <span class="variable">datagramSocket</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DatagramSocket</span>(<span class="number">9999</span>);</span><br><span class="line">    <span class="comment">// 2.构建 DatagramPacket 对象，准备接收数据</span></span><br><span class="line">    <span class="type">byte</span>[] buf = <span class="keyword">new</span> <span class="title class_">byte</span>[<span class="number">1024</span>];</span><br><span class="line">    <span class="type">DatagramPacket</span> <span class="variable">datagramPacket</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DatagramPacket</span>(buf, buf.length);</span><br><span class="line">    <span class="comment">// 3.调用接收方法，将接收到的 DatagramPacket 对象填充到本地 DatagramPacket</span></span><br><span class="line">    <span class="comment">//   如端口 9999 没有接收到数据则阻塞</span></span><br><span class="line">    System.out.println(<span class="string">&quot;9999 等待数据&quot;</span>);</span><br><span class="line">    datagramSocket.receive(datagramPacket);</span><br><span class="line">    <span class="comment">// 4.拆包取出数据</span></span><br><span class="line">    <span class="type">int</span> <span class="variable">length</span> <span class="operator">=</span> datagramPacket.getLength();</span><br><span class="line">    <span class="type">byte</span>[] data = datagramPacket.getData();</span><br><span class="line">    System.out.println(<span class="keyword">new</span> <span class="title class_">String</span>(data, <span class="number">0</span>, length));</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 5.回消息</span></span><br><span class="line">    <span class="type">byte</span>[] bytes = <span class="string">&quot;好的，马到&quot;</span>.getBytes();</span><br><span class="line">    datagramPacket = <span class="keyword">new</span> <span class="title class_">DatagramPacket</span>(bytes, bytes.length, InetAddress.getByName(<span class="string">&quot;192.168.0.110&quot;</span>), <span class="number">8888</span>);</span><br><span class="line">    datagramSocket.send(datagramPacket);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 6.关闭资源</span></span><br><span class="line">    datagramSocket.close();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 发送端</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">    <span class="comment">// 1.创建 DatagramSocket 对象，端口为 8888</span></span><br><span class="line">    <span class="type">DatagramSocket</span> <span class="variable">datagramSocket</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DatagramSocket</span>(<span class="number">8888</span>);</span><br><span class="line">    <span class="comment">// 2.构建 DatagramPacket 对象，装包</span></span><br><span class="line">    <span class="type">byte</span>[] bytes = <span class="string">&quot;今晚吃火锅&quot;</span>.getBytes();</span><br><span class="line">    <span class="type">DatagramPacket</span> <span class="variable">datagramPacket</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DatagramPacket</span>(bytes, bytes.length, InetAddress.getByName(<span class="string">&quot;192.168.0.110&quot;</span>), <span class="number">9999</span>);</span><br><span class="line">    <span class="comment">// 3.发送消息</span></span><br><span class="line">    datagramSocket.send(datagramPacket);</span><br><span class="line">    System.out.println(<span class="string">&quot;消息已发送&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 4.接收回信</span></span><br><span class="line">    <span class="type">byte</span>[] reply = <span class="keyword">new</span> <span class="title class_">byte</span>[<span class="number">1024</span>];</span><br><span class="line">    datagramPacket = <span class="keyword">new</span> <span class="title class_">DatagramPacket</span>(reply, reply.length);</span><br><span class="line">    datagramSocket.receive(datagramPacket);</span><br><span class="line">    <span class="type">int</span> <span class="variable">length</span> <span class="operator">=</span> datagramPacket.getLength();</span><br><span class="line">    <span class="type">byte</span>[] data = datagramPacket.getData();</span><br><span class="line">    System.out.println(<span class="keyword">new</span> <span class="title class_">String</span>(data, <span class="number">0</span>, length));</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 5.关闭资源</span></span><br><span class="line">    datagramSocket.close();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li>
</ul>
<h3 id="反射机制"><a href="#反射机制" class="headerlink" title="反射机制"></a>反射机制</h3><ul>
<li><p>设计模式 <code>ocp原则</code>（开闭原则）：不修改源码的情况下扩容功能</p>
</li>
<li><p>允许程序在执行时借助与 Reflection API 取得任何类的内部信息（如成员变量、构造器、成员方法等），不能操作对象的属性及方法</p>
</li>
<li><p>常用于设计模式和底层框架</p>
</li>
<li><p>加载类完成后，在堆中就产生了一个 Class 类型的对象（一个类只有一个 Class 对象）。这个对象包含类的完整结构信息，可通过这个对象得到类的结构（这个对象就像一面镜子，所以称为反射）</p>
</li>
<li><p>反射机制示意图</p>
<p><a target="_blank" rel="noopener" href="https://imgtu.com/i/HKdDmQ"><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/%E9%9B%B6%E5%9F%BA%E7%A1%80%E5%AD%A6Java/HKdDmQ.png" alt="HKdDmQ.png"></a></p>
</li>
<li><p>反射机制功能</p>
<ul>
<li>在运行时判断任意一个对象所属的类</li>
<li>在运行时构造任意一个类的对象</li>
<li>在运行时得到任意一个类所具有的成员变量和方法</li>
<li>在运行时调用任意一个对象的成员变量和方法</li>
<li>生成动态代理</li>
</ul>
</li>
<li><p>反射相关的类</p>
<ul>
<li><p><code>java.lang.Class</code>：代表一个类，Class 对象表示某个类加载后在堆中的对象</p>
</li>
<li><p><code>java.lang.reflect.Method</code>：代表类的方法，Method 对象表示某个类的方法</p>
</li>
<li><p><code>java.lang.reflect.Field</code>：代表类的成员变量，Field 对象表示某个类的成员变量</p>
</li>
<li><p><code>java.lang,reflect.Constructor</code>：代表类的构造方法，Constructor 对象表示某个类的构造器</p>
<p>传统写法：<code>对象.成员变量</code>；反射：<code>成员方法对象.invoke(对象)</code>、<code>成员变量对象.get(对象)</code></p>
</li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Cat</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">String</span> <span class="variable">name</span> <span class="operator">=</span> <span class="string">&quot;招财猫&quot;</span>;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="variable">age</span> <span class="operator">=</span> <span class="number">8</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Cat</span><span class="params">()</span> &#123;&#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Cat</span><span class="params">(String name)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.name = name;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">cry</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(name + <span class="string">&quot;喵喵叫...&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 两个类写在不同文件！！！！！</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Main</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">classFullName</span> <span class="operator">=</span> <span class="string">&quot;Cat&quot;</span>;</span><br><span class="line">        <span class="type">String</span> <span class="variable">methodName</span> <span class="operator">=</span> <span class="string">&quot;cry&quot;</span>;</span><br><span class="line"></span><br><span class="line">        <span class="type">Class</span> <span class="variable">cls</span> <span class="operator">=</span> Class.forName(classFullName);</span><br><span class="line">        <span class="comment">// 1.获取类对象实例</span></span><br><span class="line">        <span class="type">Object</span> <span class="variable">obj</span> <span class="operator">=</span> cls.newInstance();</span><br><span class="line">        System.out.println(<span class="string">&quot;obj 的运行类型=&quot;</span> + obj.getClass());      <span class="comment">// obj 的运行类型=class Cat</span></span><br><span class="line">        <span class="comment">// 2.获取 Method 对象</span></span><br><span class="line">        <span class="type">Method</span> <span class="variable">method</span> <span class="operator">=</span> cls.getMethod(methodName);</span><br><span class="line">        <span class="comment">// 调用该对象的方法</span></span><br><span class="line">        method.invoke(obj);                         <span class="comment">// 招财猫喵喵叫...</span></span><br><span class="line">        <span class="comment">// 3.获取 Field 对象</span></span><br><span class="line">        <span class="comment">// Field nameField = cls.getField(&quot;name&quot;);   错误，不能得到私有属性</span></span><br><span class="line">        <span class="type">Field</span> <span class="variable">ageField</span> <span class="operator">=</span> cls.getField(<span class="string">&quot;age&quot;</span>);</span><br><span class="line">        System.out.println(ageField.get(obj));      <span class="comment">// 8</span></span><br><span class="line">        <span class="comment">// 4.获取构造器类型</span></span><br><span class="line">        <span class="type">Constructor</span> <span class="variable">constructor1</span> <span class="operator">=</span> cls.getConstructor();            <span class="comment">// 无参构造器</span></span><br><span class="line">        System.out.println(constructor1);                           <span class="comment">// public Cat()</span></span><br><span class="line">        <span class="type">Constructor</span> <span class="variable">constructor2</span> <span class="operator">=</span> cls.getConstructor(String.class);<span class="comment">// 有参构造器，传入形参列表的 Class 对象</span></span><br><span class="line">        System.out.println(constructor2);                           <span class="comment">// public Cat(java.lang.String)</span></span><br><span class="line">        <span class="comment">// 用构造器对象创建对象实例</span></span><br><span class="line">        <span class="type">Cat</span> <span class="variable">obj2</span> <span class="operator">=</span> (Cat)constructor1.newInstance();</span><br><span class="line">        obj2.cry();                                                    <span class="comment">// 招财猫喵喵叫...</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>反射优缺点</p>
<ul>
<li>优点：可以动态创建和使用对象（这是框架底层核心），使用灵活，没有反射机制，框架技术就失去底层支撑</li>
<li>缺点：使用反射基本是解释执行，对执行速度有影响</li>
<li>反射调优优化：Method、Field、Constructor 均有 <code>setAccessible</code> 方法，可用于开启 &#x2F; 关闭访问检查的开关。设为 true 表关闭访问检查，可提高效率</li>
</ul>
</li>
<li><p>Class 类分析</p>
<ul>
<li><p>Class 类也是类，继承自 Object 类，实现了 Serializable 接口，只是功能特殊</p>
</li>
<li><p>Class 类对象不是 new 出来的，而是系统创建的</p>
</li>
<li><p>因为类只加载一次，所以一个类只对应一个 Class 对象，在内存中只有一份</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="type">Class</span> <span class="variable">cls1</span> <span class="operator">=</span> Class.forName(<span class="string">&quot;Cat&quot;</span>);</span><br><span class="line"><span class="type">Class</span> <span class="variable">cls2</span> <span class="operator">=</span> Class.forName(<span class="string">&quot;Cat&quot;</span>);</span><br><span class="line"><span class="comment">// 两者 hashCode 相等，是同一个 Class 类</span></span><br><span class="line">System.out.println(cls1.hashCode());</span><br><span class="line">System.out.println(cls2.hashCode());</span><br></pre></td></tr></table></figure>
</li>
<li><p>每个类都会知道自己实例所关联的 Class 类对象</p>
</li>
<li><p>通过 Class 对象可以调用一系列 API，得到该类的完整结构 </p>
</li>
<li><p>Class 对象是存放在堆中的</p>
</li>
<li><p>类的字节码二进制数据是存放在方法区的，有的敌方称为类的元数据（包括方法代码、变量名、方法名、访问权限等）</p>
</li>
<li><p>Class 类常用方法</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Cat</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">String</span> <span class="variable">name</span> <span class="operator">=</span> <span class="string">&quot;招财猫&quot;</span>;</span><br><span class="line">    <span class="keyword">protected</span> <span class="type">String</span> <span class="variable">protectedTest</span> <span class="operator">=</span> <span class="string">&quot;protected&quot;</span>;</span><br><span class="line">    <span class="type">String</span> <span class="variable">defaultTest</span> <span class="operator">=</span> <span class="string">&quot;default&quot;</span>;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">double</span> <span class="variable">price</span> <span class="operator">=</span> <span class="number">3000.0</span>;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="variable">age</span> <span class="operator">=</span> <span class="number">8</span>;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">toString</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;Cat&#123;&quot;</span> +</span><br><span class="line">                <span class="string">&quot;name=&#x27;&quot;</span> + name + <span class="string">&#x27;\&#x27;&#x27;</span> +</span><br><span class="line">                <span class="string">&quot;, age=&quot;</span> + age +</span><br><span class="line">                <span class="string">&#x27;&#125;&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">    <span class="comment">// &lt;?&gt; 表示不确定的 Java 类型</span></span><br><span class="line">    Class&lt;?&gt; cls = Class.forName(<span class="string">&quot;Cat&quot;</span>);</span><br><span class="line">    <span class="comment">// 1.输出 cls</span></span><br><span class="line">    System.out.println(cls);                <span class="comment">// 显示 cls 对象是哪个类的 Class 对象: Cat</span></span><br><span class="line">    System.out.println(cls.getClass());     <span class="comment">// 输出 cls 运行类型: java.lang.Class</span></span><br><span class="line">    <span class="comment">// 2.得到包名</span></span><br><span class="line">    System.out.println(cls.getPackage().getName());     <span class="comment">// 由于直接在 src 下，所以包名为空</span></span><br><span class="line">    System.out.println(cls.getPackageName());           <span class="comment">// 由于直接在 src 下，所以包名为空</span></span><br><span class="line">    <span class="comment">// 3.得到全类名</span></span><br><span class="line">    System.out.println(cls.getName());      <span class="comment">// Cat</span></span><br><span class="line">    <span class="comment">// 4.通过 cls 创建对象实例</span></span><br><span class="line">    <span class="type">Cat</span> <span class="variable">cat</span> <span class="operator">=</span> (Cat)cls.newInstance();</span><br><span class="line">    System.out.println(cat);</span><br><span class="line">    <span class="comment">// 5.得到公有属性 age</span></span><br><span class="line">    <span class="type">Field</span> <span class="variable">ageField</span> <span class="operator">=</span> cls.getField(<span class="string">&quot;age&quot;</span>);</span><br><span class="line">    System.out.println(ageField.get(cat));</span><br><span class="line">    <span class="comment">// 6.给属性赋值</span></span><br><span class="line">    ageField.set(cat, <span class="number">10</span>);</span><br><span class="line">    System.out.println(ageField.get(cat));</span><br><span class="line">    <span class="comment">// 7.得到所有公有属性</span></span><br><span class="line">    Field[] fields = cls.getFields();</span><br><span class="line">    <span class="keyword">for</span> (Field field : fields) &#123;</span><br><span class="line">        System.out.println(field.getName() + <span class="string">&quot; = &quot;</span> + field.get(cat));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>获取 Class 对象的 6 种方式（所获取的均为同一个 Class 对象）</p>
<ul>
<li><code>Class.forName(&quot;classFullPath&quot;)</code>：适用于已知全类名，多用于配置文件</li>
<li><code>类名.class</code>：适用于已知具体的类，多用于参数传递，如得到对应构造器对象；该方式最为安全可靠，程序性能最高</li>
<li><code>对象.getClass()</code>：适用于已有对象实例</li>
<li>通过类加载器</li>
<li><code>基本数据类型.class</code></li>
<li><code>基本数据对应包装类.TYPE</code></li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">    <span class="type">String</span> <span class="variable">classFullName</span> <span class="operator">=</span> <span class="string">&quot;Cat&quot;</span>;</span><br><span class="line">    <span class="comment">// 1.Class.forName(&quot;classFullPath&quot;)</span></span><br><span class="line">    Class&lt;?&gt; cls1 = Class.forName(classFullName);</span><br><span class="line">    System.out.println(cls1);</span><br><span class="line">    <span class="comment">// 2.类名.class</span></span><br><span class="line">    Class&lt;Cat&gt; cls2 = Cat.class;</span><br><span class="line">    System.out.println(cls2);</span><br><span class="line">    <span class="comment">// 3.对象.getClass()，即运行类型</span></span><br><span class="line">    <span class="type">Cat</span> <span class="variable">cat</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Cat</span>();</span><br><span class="line">    Class&lt;? <span class="keyword">extends</span> <span class="title class_">Cat</span>&gt; cls3 = cat.getClass();</span><br><span class="line">    System.out.println(cls3);</span><br><span class="line">    <span class="comment">// 4.通过类加载器（有 4 种）</span></span><br><span class="line">    <span class="type">ClassLoader</span> <span class="variable">classLoader</span> <span class="operator">=</span> cat.getClass().getClassLoader();</span><br><span class="line">    Class&lt;?&gt; cls4 = classLoader.loadClass(classFullName);</span><br><span class="line">    System.out.println(cls4);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 5.基本数据类型</span></span><br><span class="line">    Class&lt;Integer&gt; integerClass = <span class="type">int</span>.class;</span><br><span class="line">    Class&lt;Character&gt; characterClass = <span class="type">char</span>.class;</span><br><span class="line">    Class&lt;String&gt; stringClass = String.class;</span><br><span class="line">    System.out.println(integerClass);</span><br><span class="line">    System.out.println(characterClass);</span><br><span class="line">    System.out.println(stringClass);</span><br><span class="line">    <span class="comment">// 6.基本数据类型的包装类</span></span><br><span class="line">    Class&lt;Integer&gt; integerType = Integer.TYPE;</span><br><span class="line">    Class&lt;Boolean&gt; booleanType = Boolean.TYPE;</span><br><span class="line">    System.out.println(integerType);</span><br><span class="line">    System.out.println(booleanType);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>哪些类型有 Class 对象</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">Class&lt;String&gt; stringClass = String.class;                   <span class="comment">// 外部类</span></span><br><span class="line">Class&lt;Serializable&gt; serializableClass = Serializable.class; <span class="comment">// 接口</span></span><br><span class="line">Class&lt;Integer[]&gt; vectorClass = Integer[].class;             <span class="comment">// 数组</span></span><br><span class="line">Class&lt;<span class="type">float</span>[][]&gt; vector2Class = <span class="type">float</span>[][].class;            <span class="comment">// 二维数组</span></span><br><span class="line">Class&lt;Deprecated&gt; deprecatedClass = Deprecated.class;       <span class="comment">// 注解</span></span><br><span class="line">Class&lt;Thread.State&gt; stateClass = Thread.State.class;        <span class="comment">// 枚举</span></span><br><span class="line">Class&lt;Long&gt; longClass = <span class="type">long</span>.class;                         <span class="comment">// 基本数据类型</span></span><br><span class="line">Class&lt;Void&gt; voidClass = <span class="keyword">void</span>.class;                         <span class="comment">// 基本数据类型</span></span><br><span class="line">Class&lt;Class&gt; classClass = Class.class;                      <span class="comment">// Class 也是一个外部类</span></span><br><span class="line"><span class="comment">// 四种内部类也有，不再演示</span></span><br></pre></td></tr></table></figure>
</li>
<li><p><strong>类加载过程（极其重要 <a target="_blank" rel="noopener" href="https://www.bilibili.com/video/BV1fh411y7R8?p=720">P720-P723</a>）</strong></p>
<ul>
<li><p><strong>静态加载</strong>：编译时加载相关的类，如果没有则报错，依赖性太强</p>
</li>
<li><p><strong>动态加载</strong>：运行时加载需要的类，如果运行时不用该类即使该类不存在也不报错，依赖性低</p>
</li>
<li><p>类加载时机：</p>
<ul>
<li>创建类时（new）：<strong>静态加载</strong></li>
<li>当子类被加载时：<strong>静态加载</strong></li>
<li>调用类中的静态成员（属性、方法、代码块、内部类）时：<strong>静态加载</strong></li>
<li>通过反射：<strong>动态加载</strong></li>
</ul>
</li>
<li><p>类加载流程图</p>
<p><a target="_blank" rel="noopener" href="https://imgtu.com/i/HMM76I"><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/%E9%9B%B6%E5%9F%BA%E7%A1%80%E5%AD%A6Java/HMM76I.png" alt="HMM76I.png"></a></p>
<p><a target="_blank" rel="noopener" href="https://imgtu.com/i/HMQT5F"><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/%E9%9B%B6%E5%9F%BA%E7%A1%80%E5%AD%A6Java/HMQT5F.png" alt="HMQT5F.png"></a></p>
</li>
<li><p>类加载五个阶段（类加载过程都与实例对象无关，只跟静态成员相关）</p>
<ul>
<li><p>加载阶段：将字节码从不同的数据源（class 文件、jar 包、甚至是网络）著那位二进制字节流加载到内存中，并生成一个代表该类的 <code>java.lang.Class</code> 对象</p>
</li>
<li><p>连接阶段</p>
<ul>
<li><p>验证：为确保 Class 文件的字节流中包含的信息符合当前虚拟机的要求，且不会危害虚拟机自身的安全。包括：文件格式验证（是否以魔数 <code>0xcafebabe</code> 开头）、元数据验证、字节码验证和符号引用验证</p>
<p>可以用 <code>-Xverify:none</code> 参数关闭大部分的类验证措施，加快类加载速度</p>
</li>
<li><p>准备：JVM 会在该阶段对静态变量分配内存并初始化（对应数据类型的默认初始化）。这些变量所使用的内存都在方法区中进行分配</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">A</span> &#123;</span><br><span class="line">    <span class="comment">// n1 非静态变量，在准备阶段不分配内存</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="variable">n1</span> <span class="operator">=</span> <span class="number">10</span>;</span><br><span class="line">    <span class="comment">// n2 是静态变量，准备阶段分配内存，初始化为 0</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="type">int</span> <span class="variable">n2</span> <span class="operator">=</span> <span class="number">20</span>;</span><br><span class="line">    <span class="comment">// n2 是静态常量，准备阶段分配内存，初始化为 30</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> <span class="variable">n3</span> <span class="operator">=</span> <span class="number">30</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>解析：JVM 将常量池内的符号引用替换为直接引用</p>
</li>
</ul>
</li>
<li><p>初始化阶段：真正执行类中定义的 Java 程序代码，此阶段是执行 <code>&lt;clinit&gt;()</code> 方法的过程</p>
<ul>
<li><code>&lt;clinit&gt;()</code> 方法是由编译器按语句在源文件中出现的<strong>顺序</strong>，一次自动收集类中所有<strong>静态变量</strong>的赋值语句和<strong>静态代码块</strong>中的语句进行合并</li>
<li>JVM 保证一个类的 <code>&lt;clinit&gt;()</code> 方法在多线程环境中被正确地加锁、同步，如果多个线程同时初始化一个类，保证只会有一个线程执行该方法，其他线程都需阻塞等待，直到该方法执行完毕（<strong>保证某个类在内存中只有一个 Class 类对象</strong>）</li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">	&lt;clinit&gt;() &#123;</span></span><br><span class="line"><span class="comment">		System.out.println(&quot;B 的静态代码块被执行&quot;);</span></span><br><span class="line"><span class="comment">        // 按顺序进行初始化，num = 300; 没有意义，会被忽略</span></span><br><span class="line"><span class="comment">        num = 100;</span></span><br><span class="line"><span class="comment">	&#125;</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用类的静态属性，会导致类加载</span></span><br><span class="line"><span class="comment">// 没有实例化 B 对象，所以不会调用构造器</span></span><br><span class="line">System.out.println(B.num);		<span class="comment">// 100</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">B</span> &#123;</span><br><span class="line">    <span class="keyword">static</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;B 的静态代码块被执行&quot;</span>);</span><br><span class="line">        num = <span class="number">300</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">static</span> <span class="type">int</span> <span class="variable">num</span> <span class="operator">=</span> <span class="number">100</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">B</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;B() 构造器被执行&quot;</span>)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li><p>通过反射获取类结构信息</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 第一组方法 API</span></span><br><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">api_01</span><span class="params">()</span> <span class="keyword">throws</span> ClassNotFoundException, NoSuchMethodException &#123;</span><br><span class="line">    <span class="comment">// 得到 Class 对象</span></span><br><span class="line">    Class&lt;?&gt; personCls = Class.forName(<span class="string">&quot;Person&quot;</span>);</span><br><span class="line">    <span class="comment">// 1.getName:获取全类名，由于直接在 src 下，所以无前缀</span></span><br><span class="line">    System.out.println(personCls.getName());        <span class="comment">// Person</span></span><br><span class="line">    <span class="comment">// 2.getSimpleName:获取简单类名</span></span><br><span class="line">    System.out.println(personCls.getSimpleName());  <span class="comment">// Person</span></span><br><span class="line">    <span class="comment">// 3.getFields:获取所有public修饰的属性，包含本类以及父类的(不仅限于直接父类)</span></span><br><span class="line">    Field[] fields = personCls.getFields();</span><br><span class="line">    <span class="keyword">for</span> (Field field : fields) &#123;<span class="comment">//增强for</span></span><br><span class="line">        System.out.println(<span class="string">&quot;本类以及父类的 public 属性=&quot;</span> + field.getName());</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 4.getDeclaredFields:获取本类中所有属性</span></span><br><span class="line">    Field[] declaredFields = personCls.getDeclaredFields();</span><br><span class="line">    <span class="keyword">for</span> (Field declaredField : declaredFields) &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;本类中所有属性=&quot;</span> + declaredField.getName());</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 5.getMethods:获取所有public修饰的方法，包含本类以及父类的(不仅限于直接父类)</span></span><br><span class="line">    Method[] methods = personCls.getMethods();</span><br><span class="line">    <span class="keyword">for</span> (Method method : methods) &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;本类以及父类的 public 方法=&quot;</span> + method.getName());</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 6.getDeclaredMethods:获取本类中所有方法</span></span><br><span class="line">    Method[] declaredMethods = personCls.getDeclaredMethods();</span><br><span class="line">    <span class="keyword">for</span> (Method declaredMethod : declaredMethods) &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;本类中所有方法=&quot;</span> + declaredMethod.getName());</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 7.getConstructors: 获取所有public修饰的构造器，仅包含本类，不含父类</span></span><br><span class="line">    Constructor&lt;?&gt;[] constructors = personCls.getConstructors();</span><br><span class="line">    <span class="keyword">for</span> (Constructor&lt;?&gt; constructor : constructors) &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;本类的 public 构造器=&quot;</span> + constructor.getName());</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 8.getDeclaredConstructors:获取本类中所有构造器</span></span><br><span class="line">    Constructor&lt;?&gt;[] declaredConstructors = personCls.getDeclaredConstructors();</span><br><span class="line">    <span class="keyword">for</span> (Constructor&lt;?&gt; declaredConstructor : declaredConstructors) &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;本类中所有构造器=&quot;</span> + declaredConstructor.getName());</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 9.getPackage:以 Package 形式返回包信息，由于在 src 下，所以只输出 package</span></span><br><span class="line">    System.out.println(personCls.getPackage());         <span class="comment">// package</span></span><br><span class="line">    <span class="comment">// 10.getSuperClass:以Class形式返回父类信息</span></span><br><span class="line">    Class&lt;?&gt; superclass = personCls.getSuperclass();</span><br><span class="line">    System.out.println(<span class="string">&quot;父类的class对象=&quot;</span> + superclass);</span><br><span class="line">    <span class="comment">// 11.getInterfaces:以Class[]形式返回接口信息</span></span><br><span class="line">    Class&lt;?&gt;[] interfaces = personCls.getInterfaces();</span><br><span class="line">    <span class="keyword">for</span> (Class&lt;?&gt; anInterface : interfaces) &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;接口信息=&quot;</span> + anInterface);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 12.getAnnotations:以Annotation[] 形式返回注解信息</span></span><br><span class="line">    Annotation[] annotations = personCls.getAnnotations();</span><br><span class="line">    <span class="keyword">for</span> (Annotation annotation : annotations) &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;注解信息=&quot;</span> + annotation);     <span class="comment">// 注解</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 第二组方法 API</span></span><br><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">api_02</span><span class="params">()</span> <span class="keyword">throws</span> ClassNotFoundException, NoSuchMethodException &#123;</span><br><span class="line">    <span class="comment">// 得到 Class 对象</span></span><br><span class="line">    Class&lt;?&gt; personCls = Class.forName(<span class="string">&quot;Person&quot;</span>);</span><br><span class="line">    <span class="comment">// getDeclaredFields:获取本类中所有属性</span></span><br><span class="line">    <span class="comment">// 规定说明: 默认修饰符-0 ， public-1 ，private-2 ，protected-4 , static-8 ，final-16</span></span><br><span class="line">    <span class="comment">// protected static int age;  则返回 4 + 8 = 12</span></span><br><span class="line">    Field[] declaredFields = personCls.getDeclaredFields();</span><br><span class="line">    <span class="keyword">for</span> (Field declaredField : declaredFields) &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;本类中所有属性=&quot;</span> + declaredField.getName()</span><br><span class="line">                           + <span class="string">&quot; 该属性的修饰符值=&quot;</span> + declaredField.getModifiers()</span><br><span class="line">                           + <span class="string">&quot; 该属性的类型=&quot;</span> + declaredField.getType());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// getDeclaredMethods:获取本类中所有方法</span></span><br><span class="line">    Method[] declaredMethods = personCls.getDeclaredMethods();</span><br><span class="line">    <span class="keyword">for</span> (Method declaredMethod : declaredMethods) &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;本类中所有方法=&quot;</span> + declaredMethod.getName()</span><br><span class="line">                           + <span class="string">&quot; 该方法的访问修饰符值=&quot;</span> + declaredMethod.getModifiers()</span><br><span class="line">                           + <span class="string">&quot; 该方法返回类型&quot;</span> + declaredMethod.getReturnType());</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 输出当前这个方法的形参数组情况</span></span><br><span class="line">        Class&lt;?&gt;[] parameterTypes = declaredMethod.getParameterTypes();</span><br><span class="line">        <span class="keyword">for</span> (Class&lt;?&gt; parameterType : parameterTypes) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;该方法的形参类型=&quot;</span> + parameterType);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// getDeclaredConstructors:获取本类中所有构造器</span></span><br><span class="line">    Constructor&lt;?&gt;[] declaredConstructors = personCls.getDeclaredConstructors();</span><br><span class="line">    <span class="keyword">for</span> (Constructor&lt;?&gt; declaredConstructor : declaredConstructors) &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;====================&quot;</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;本类中所有构造器=&quot;</span> + declaredConstructor.getName());<span class="comment">//这里老师只是输出名</span></span><br><span class="line"></span><br><span class="line">        Class&lt;?&gt;[] parameterTypes = declaredConstructor.getParameterTypes();</span><br><span class="line">        <span class="keyword">for</span> (Class&lt;?&gt; parameterType : parameterTypes) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;该构造器的形参类型=&quot;</span> + parameterType);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">A</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> String hobby;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">hi</span><span class="params">()</span> &#123;&#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">A</span><span class="params">()</span> &#123;&#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">A</span><span class="params">(String name)</span> &#123;&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">interface</span> <span class="title class_">IA</span> &#123;&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">interface</span> <span class="title class_">IB</span> &#123;&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Deprecated</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Person</span> <span class="keyword">extends</span> <span class="title class_">A</span> <span class="keyword">implements</span> <span class="title class_">IA</span>, IB &#123;</span><br><span class="line">    <span class="comment">//属性</span></span><br><span class="line">    <span class="keyword">public</span> String name;</span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">static</span> <span class="type">int</span> age; <span class="comment">// 4 + 8 = 12</span></span><br><span class="line">    String job;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">double</span> sal;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//构造器</span></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Person</span><span class="params">()</span> &#123;&#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Person</span><span class="params">(String name)</span> &#123;&#125;</span><br><span class="line">    <span class="comment">//私有的</span></span><br><span class="line">    <span class="keyword">private</span> <span class="title function_">Person</span><span class="params">(String name, <span class="type">int</span> age)</span> &#123;&#125;</span><br><span class="line">    <span class="comment">//方法</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">m1</span><span class="params">(String name, <span class="type">int</span> age, <span class="type">double</span> sal)</span> &#123;&#125;</span><br><span class="line">    <span class="keyword">protected</span> String <span class="title function_">m2</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">m3</span><span class="params">()</span> &#123;&#125;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">m4</span><span class="params">()</span> &#123;&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>反射暴破创建实例</p>
<p>暴破机制，可以访问私有成员（反射面前，一切都是纸老虎）</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 1.获取 Class 对象</span></span><br><span class="line">Class&lt;?&gt; userClass = Class.forName(<span class="string">&quot;User&quot;</span>);</span><br><span class="line"><span class="comment">// 2.通过 public 无参构造器创建实例</span></span><br><span class="line"><span class="type">Object</span> <span class="variable">obj1</span> <span class="operator">=</span> userClass.newInstance();</span><br><span class="line">System.out.println(obj1);</span><br><span class="line"><span class="comment">// 3.通过 public 有参构造器创建实例</span></span><br><span class="line">Constructor&lt;?&gt; publicConstructor = userClass.getConstructor(String.class);</span><br><span class="line"><span class="comment">// 获取构造器实例，通过构造器传入实参</span></span><br><span class="line"><span class="type">Object</span> <span class="variable">obj2</span> <span class="operator">=</span> publicConstructor.newInstance(<span class="string">&quot;try&quot;</span>);</span><br><span class="line">System.out.println(obj2);</span><br><span class="line"><span class="comment">// 4.通过 private 有参构造器创建实例</span></span><br><span class="line">Constructor&lt;?&gt; privateConstructor = userClass.getDeclaredConstructor(<span class="type">int</span>.class, String.class);</span><br><span class="line"><span class="comment">// 暴破，使用反射可以使用 private 构造器，否则 private 不给创建</span></span><br><span class="line">privateConstructor.setAccessible(<span class="literal">true</span>);</span><br><span class="line"><span class="type">Object</span> <span class="variable">obj3</span> <span class="operator">=</span> privateConstructor.newInstance(<span class="number">18</span>, <span class="string">&quot;try&quot;</span>);</span><br><span class="line">System.out.println(obj3);</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">User</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> <span class="variable">age</span> <span class="operator">=</span> <span class="number">22</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">String</span> <span class="variable">name</span> <span class="operator">=</span> <span class="string">&quot;zcm&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 无参 public</span></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">User</span><span class="params">()</span> &#123;&#125;</span><br><span class="line">    <span class="comment">// 有参 public</span></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">User</span><span class="params">(String name)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.name = name;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 有参 private</span></span><br><span class="line">    <span class="keyword">private</span> <span class="title function_">User</span><span class="params">(<span class="type">int</span> age, String name)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.age = age;</span><br><span class="line">        <span class="built_in">this</span>.name = name;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">toString</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;User&#123;&quot;</span> +</span><br><span class="line">                <span class="string">&quot;age=&quot;</span> + age +</span><br><span class="line">                <span class="string">&quot;, name=&#x27;&quot;</span> + name + <span class="string">&#x27;\&#x27;&#x27;</span> +</span><br><span class="line">                <span class="string">&#x27;&#125;&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>反射暴破操作属性</p>
<p>如果是静态属性，则 <code>set</code>、<code>get</code> 方法中直接传入 <code>null</code></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 得到 Class 对象</span></span><br><span class="line">Class&lt;?&gt; studentClass = Class.forName(<span class="string">&quot;Student&quot;</span>);</span><br><span class="line"><span class="comment">// 1.创建 Student 对象</span></span><br><span class="line"><span class="type">Object</span> <span class="variable">obj</span> <span class="operator">=</span> studentClass.newInstance();</span><br><span class="line"><span class="comment">// 2.反射得到 public age</span></span><br><span class="line"><span class="type">Field</span> <span class="variable">ageField</span> <span class="operator">=</span> studentClass.getField(<span class="string">&quot;age&quot;</span>);</span><br><span class="line"><span class="comment">// 通过反射操作 public 属性</span></span><br><span class="line">ageField.set(obj, <span class="number">18</span>);</span><br><span class="line">System.out.println(obj);        <span class="comment">// Student [age=18, name=null]</span></span><br><span class="line">System.out.println(ageField.get(obj));     <span class="comment">// 18</span></span><br><span class="line"><span class="comment">// 3.反射得到 private static name</span></span><br><span class="line"><span class="type">Field</span> <span class="variable">nameField</span> <span class="operator">=</span> studentClass.getDeclaredField(<span class="string">&quot;name&quot;</span>);</span><br><span class="line"><span class="comment">// 暴破，通过反射操作 private 属性</span></span><br><span class="line">nameField.setAccessible(<span class="literal">true</span>);</span><br><span class="line">nameField.set(obj, <span class="string">&quot;zcm&quot;</span>);</span><br><span class="line">System.out.println(obj);        <span class="comment">// Student [age=18, name=zcm]</span></span><br><span class="line">System.out.println(nameField.get(obj));     <span class="comment">// zcm</span></span><br><span class="line"><span class="comment">// 因为 name 为静态属性，所以可以如下修改</span></span><br><span class="line">nameField.set(<span class="literal">null</span>, <span class="string">&quot;try&quot;</span>);</span><br><span class="line">System.out.println(obj);        <span class="comment">// Student [age=18, name=try]</span></span><br><span class="line">System.out.println(nameField.get(<span class="literal">null</span>));    <span class="comment">// try</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Student</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> age;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> String name;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Student</span><span class="params">()</span> &#123;&#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">toString</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;Student [age=&quot;</span> + age + <span class="string">&quot;, name=&quot;</span> + name + <span class="string">&quot;]&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>反射暴破操作方法（基本同上）</p>
<p>在反射中，如果方法有返回值，统一返回 Object</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 得到 Class 对象</span></span><br><span class="line">Class&lt;?&gt; bossClass = Class.forName(<span class="string">&quot;Boss&quot;</span>);</span><br><span class="line"><span class="comment">// 创建 Boss 对象</span></span><br><span class="line"><span class="type">Object</span> <span class="variable">obj</span> <span class="operator">=</span> bossClass.newInstance();</span><br><span class="line"><span class="comment">// 1.反射调用 public hi()</span></span><br><span class="line"><span class="type">Method</span> <span class="variable">hiMethod</span> <span class="operator">=</span> bossClass.getMethod(<span class="string">&quot;hi&quot;</span>, String.class);</span><br><span class="line">hiMethod.invoke(obj, <span class="string">&quot;zcm&quot;</span>);            <span class="comment">// hi zcm</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 2.反射调用 private static say()</span></span><br><span class="line"><span class="type">Method</span> <span class="variable">sayMethod</span> <span class="operator">=</span> bossClass.getDeclaredMethod(<span class="string">&quot;say&quot;</span>, <span class="type">int</span>.class, String.class, <span class="type">char</span>.class);</span><br><span class="line"><span class="comment">// 暴破，通过反射调用 private 方法</span></span><br><span class="line">sayMethod.setAccessible(<span class="literal">true</span>);</span><br><span class="line">System.out.println(sayMethod.invoke(obj, <span class="number">18</span>, <span class="string">&quot;zcm&quot;</span>, <span class="string">&#x27;男&#x27;</span>));      <span class="comment">// 18 zcm 男</span></span><br><span class="line"><span class="comment">// 因为 say 为静态方法，还可如下调用</span></span><br><span class="line">System.out.println(sayMethod.invoke(<span class="literal">null</span>, <span class="number">16</span>, <span class="string">&quot;try&quot;</span>, <span class="string">&#x27;女&#x27;</span>));     <span class="comment">// 16 try 女</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 3.在反射中，如果方法有返回值，统一返回 Object</span></span><br><span class="line"><span class="type">Object</span> <span class="variable">realReturn</span> <span class="operator">=</span> sayMethod.invoke(obj, <span class="number">10</span>, <span class="string">&quot;baby&quot;</span>, <span class="string">&#x27;t&#x27;</span>);</span><br><span class="line"><span class="comment">// realReturn 的运行类型=class java.lang.String</span></span><br><span class="line">System.out.println(<span class="string">&quot;realReturn 的运行类型=&quot;</span> + realReturn.getClass());</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Boss</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> age;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> String name;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Boss</span><span class="params">()</span> &#123;&#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> String <span class="title function_">say</span><span class="params">(<span class="type">int</span> n, String s, <span class="type">char</span> c)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> n + <span class="string">&quot; &quot;</span> + s + <span class="string">&quot; &quot;</span> + c;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">hi</span><span class="params">(String s)</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;hi &quot;</span> + s);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li>
</ul>
<h3 id="数据库-MySQL"><a href="#数据库-MySQL" class="headerlink" title="数据库 MySQL"></a><a target="_blank" rel="noopener" href="https://www.runoob.com/mysql/mysql-tutorial.html">数据库 MySQL</a></h3><ul>
<li><p>启动服务再连接，连接命令：<code>mysql -h 主机IP -P 端口 -u 用户名 -p密码</code></p>
<ul>
<li>登录：<code>mysql -u root -pzcm</code>；启动：<code>net start mysql</code>；停止：<code>net stop mysql</code></li>
<li>注意大小写</li>
<li><code>-p密码</code>之间没有空格，没写密码则回车后会要求输入</li>
<li>如果没写 <code>-h</code> 主机，默认是主机</li>
<li>如果没有写 <code>-P</code> 端口，默认为 <code>my.ini</code> 中默认端口（默认 3306，实际开发中为确保安全一般进行修改）</li>
</ul>
</li>
<li><p>MySQL 的三层结构</p>
<p>数据库-表的本质是文件</p>
<p><a target="_blank" rel="noopener" href="https://imgtu.com/i/HQhBBd"><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/%E9%9B%B6%E5%9F%BA%E7%A1%80%E5%AD%A6Java/HQhBBd.png" alt="HQhBBd.png"></a></p>
</li>
<li><p>SQL 语句分类</p>
<ul>
<li>DDL：数据定义语句——create 表、库</li>
<li>DML：数据操作语句——增 insert、删 delete、改 update</li>
<li>DQL：数据查询语句——select 等</li>
<li>DCL：数据控制语句——管理数据库：如用户权限 grant、revoke</li>
</ul>
</li>
<li><p>常用语句</p>
<ul>
<li><p>通用规则</p>
<ul>
<li>windows 下的 MySQL 语句不区分大小写<ul>
<li><code>select * from table_name </code> &#x3D;&#x3D; <code>SELECT * FROM table_name</code></li>
<li>其中 <code>table_name</code> 也不区分大小写，如 <code>users</code> &#x3D;&#x3D; <code>USERS</code></li>
</ul>
</li>
<li><code>[] </code>表可选选项</li>
<li>为规避关键字，可用反引号：<code>create database ‘create’</code></li>
<li>语句用分号 <code>;</code> 结尾</li>
<li>注意中英文符号</li>
</ul>
</li>
<li><p>创建数据库</p>
<ul>
<li><code>create database db_name [charset_name] [collation_name] ...</code></li>
<li>charset_name：指定字符集名称，默认为 <code>utf8</code></li>
<li>collation_name：指定数据库字符集校对规则，默认 <code>utf8_general_ci（不区分大小写）</code>，常用 <code>utf8_bin（区分大小写）</code></li>
</ul>
</li>
<li><p>查询数据库</p>
<ul>
<li>显示数据库：<code>show databases</code></li>
<li>显示创建该数据库时所用语句：<code>show create database db_name</code></li>
<li>删除数据库：<code>drop database db_name</code></li>
</ul>
</li>
<li><p>备份恢复数据库</p>
<ul>
<li><p>备份数据库（在 DOS 命令行下执行）：<code>mysqldump -u 用户名 -p密码 -B db_name1 db_name2 db_namen &gt; (含路径)文件名.sql</code>（可同时备份多个数据库）</p>
<p>如：<code>C:\Users\zcm&gt;mysqldump -u root -p -B db01 &gt; d:\\bak.sql</code></p>
</li>
<li><p>备份数据库中的表：（在 DOS 命令行下执行）：<code>mysqldump -u 用户名 -p密码 db_name table_name1 table_name2 table_namen &gt; (含路径)文件名.sql</code>（可同时备份数据库中多个表）</p>
<p>如：<code>C:\Users\zcm&gt;mysqldump -u root -p db01 users &gt; d:\\bak.sql</code></p>
</li>
<li><p>恢复数据库（进入 MySQL 命令行再执行）：<code>source 文件名.sql</code></p>
<p>如：<code>mysql&gt; source d:\\bak.sql</code>（DOS 下的 <code>mysql</code> 可行，<code>Navicat</code> 中命令行不行）</p>
</li>
</ul>
</li>
<li><p><a target="_blank" rel="noopener" href="https://www.runoob.com/mysql/mysql-data-types.html">MySQL 常用数据类型（列类型）</a></p>
<p><strong>MySQL 支持所有标准 SQL 数值数据类型</strong></p>
<p><strong>能满足使用需求的情况下尽量选占用空间小的</strong></p>
<ul>
<li><p>数值类型：整型（常用 int） + 小数型（常用 double、decimal）</p>
<ul>
<li><p>其中 decimal(M,D) M 是小数位数（精度）的总数。D 是小数点（标度）后面的位数</p>
<p>如果 D 是 0，则值没有小数点或分数部分。M 最大为 65，D 最大为30</p>
<p>如果 D 省略则默认为 0；如果 M 省略则默认为 10</p>
</li>
</ul>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/%E9%9B%B6%E5%9F%BA%E7%A1%80%E5%AD%A6Java/HQxp9g.png" alt="HQxp9g.png"></p>
</li>
<li><p>文本类型（字符串类型，常用 char、varchar、text）+ 二进制数据类型（不常用）</p>
<ul>
<li><p>字符串可用单引号也可用双引号</p>
</li>
<li><p>char 最大为 255 字符；varchar 最大为 65535 字节（utf8 编码一个字符占 3 个字节，varchar 最大可存 21844 字符 &#x3D; (65536 - 3) &#x2F; 3），<strong>其中 <code>-3</code>是为了预留空间存放内容的长度</strong></p>
<p><code>char(4)</code> 表字符数，不论中文还是英文都最多存放 4 个字符</p>
<p><code>varchar(4)</code> 也表字符数，不论中文还是英文都最多存放 4 个字符（以定义好的编码格式存放，如 uft8 存放 4 个字符共占 12 字节）</p>
</li>
<li><p><code>char(4)</code> 是定长，存放<code>aa</code>也占用 4 个字符空间；<code>varchar(4)</code> 是变长，存放 <code>aa</code> 则实际占用空间不是 4 个字符（也不是 2 个字符， varchar 本身需要战用 <strong>1~3 个字节</strong>来记录存放内容的长度）</p>
</li>
<li><p>定长数据用 char；变长用 varchar；但是<strong>查询速度 char &gt; varchar</strong></p>
</li>
<li><p>如果 <code>varchar</code> 不够用，可使用 <code>text、mediumtext、longtext</code> 代替</p>
</li>
</ul>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/%E9%9B%B6%E5%9F%BA%E7%A1%80%E5%AD%A6Java/HQvz4S.png" alt="HQvz4S.png"></p>
</li>
<li><p>日期类型（常用 datetime、timestamp）</p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/%E9%9B%B6%E5%9F%BA%E7%A1%80%E5%AD%A6Java/HQvxN8.png" alt="HQvxN8.png"></p>
</li>
<li><p>bit 类型（位类型）：不常用</p>
<ul>
<li><code>(num bit(8))</code>：列名为 <code>num</code>，长度为 8 bit（可指定长度范围为 1~64）</li>
<li>显示按照位方式显示。如 <code>3 -&gt; 00000011</code></li>
<li>如有个值只有 0 或 1，可考虑用 bit(1) 存放，节省空间</li>
</ul>
</li>
</ul>
</li>
<li><p>创建表</p>
<ul>
<li><code>create table table_name (field1 datatype, field2 datatype, filedn datatype) character_set collate engine</code></li>
<li>field：指定列名；datatype：指定列类型（字段类型）</li>
<li>character_set：如不指定则为所在数据库字符集</li>
<li>collate：如不指定则为所在数据库校对规则</li>
<li>engine：引擎</li>
</ul>
</li>
<li><p>查询表</p>
<ul>
<li><p>删除表：<code>drop table table_name</code></p>
</li>
<li><p>查看表的结构：<code>desc table_name</code>，查看表所有列</p>
</li>
<li><p><strong>修改表</strong></p>
<ul>
<li>添加列：<code>alter table table_name add column datatype, ...</code></li>
<li>修改列：<code>alter table table_name modify column datatype, ...</code></li>
<li>删除列：<code>alter table table_name drop column, ...</code></li>
<li>修改列名：<code>alter table table_name change old_column_name new_column_name datatype, ...</code></li>
<li>修改表名：<code>rename table old_table_name to new_table_name</code></li>
<li>修改表字符集：<code>alter table table_name character set 字符集名</code>、<code>alter table table_name charset 字符集名</code></li>
</ul>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">create table emp (</span><br><span class="line">    id int,</span><br><span class="line">    name varchar(255),</span><br><span class="line">    sex char(1),</span><br><span class="line">    birthday date,</span><br><span class="line">    entry_date datetime,</span><br><span class="line">    job varchar(32),</span><br><span class="line">    salary double,</span><br><span class="line">    resume text</span><br><span class="line">) charset utf8 collate utf8_bin engine innodb;</span><br><span class="line"></span><br><span class="line">-- 在 job 后面添加一个 image 列，varchar 类型</span><br><span class="line">alter table emp </span><br><span class="line">	add image varchar(32) not null default &#x27;&#x27;</span><br><span class="line">	after job;</span><br><span class="line">	</span><br><span class="line">-- 修改 job 列，使其长度为 60</span><br><span class="line">alter table emp</span><br><span class="line">	modify job varchar(60) not null default &#x27;&#x27;;</span><br><span class="line"></span><br><span class="line">-- 删除 sex 列</span><br><span class="line">alter table emp</span><br><span class="line">	drop sex;</span><br><span class="line">	</span><br><span class="line">-- 修改表名</span><br><span class="line">rename table emp to employee;</span><br><span class="line"></span><br><span class="line">-- 修改表的字符集</span><br><span class="line">alter table employee character set utf8;</span><br><span class="line">-- 或</span><br><span class="line">alter table employee charset utf8;</span><br><span class="line"></span><br><span class="line">-- 修改列名 name 为 user_name</span><br><span class="line">alter table employee </span><br><span class="line">	change name user_name varchar(32) not null default &#x27;&#x27;;</span><br></pre></td></tr></table></figure></li>
</ul>
</li>
</ul>
</li>
<li><p>CRUD 语句：[create、read、update、delete]</p>
<ul>
<li><p>insert：<code>insert into table_name (列名，省略则按照创建顺序) values(一一对应，逗号隔开)</code></p>
<ul>
<li>插入数据应和列名数据类型一一对应。如将 <code>&#39;abc&#39;</code> 插入 <code>int</code> 类型会报错，但是可以将<code>&#39;30&#39;</code>传入<code>int</code>。<code>mysql</code> 底层会尝试将字符串转为 <code>int</code></li>
<li>数据长度应在列的规定范围内。如不能将长度为 20 的字符串传入 <code>varchar(10)</code></li>
<li>values 中列出的数据位置必须与相应列名一一对应（可以通过调整列名位置调整插入 values 顺序）</li>
<li>字符和日期都应包含在单引号 &#x2F; 双引号中</li>
<li>某列允许为空时，可以插入空值 <code>null</code></li>
<li>没有设置默认值的列对应 values 不可省略</li>
<li>可一次插入多条数据：<code>values (...), (...), (...) ...</code></li>
<li>添加数据时若所有列都添加，则可省略列名，省略则按照创建顺序；<strong>需考虑默认值</strong></li>
</ul>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">create table goods (</span><br><span class="line">	id int,</span><br><span class="line">	goods_name varchar(10),</span><br><span class="line">	price double);</span><br><span class="line">	</span><br><span class="line">insert into goods (id, goods_name, price)</span><br><span class="line">	values(10, &#x27;手机&#x27;, 2000);</span><br><span class="line"></span><br><span class="line">-- 可以通过调整列名位置调整插入 values 顺序</span><br><span class="line">insert into goods (id, price, goods_name)</span><br><span class="line">	values(10, 2000, &#x27;手机&#x27;);</span><br><span class="line"></span><br><span class="line">-- 添加数据时若所有列都添加，则可省略列名，省略则按照创建顺序</span><br><span class="line">insert into goods</span><br><span class="line">	values(20, &#x27;电脑&#x27;, 5000);</span><br><span class="line">-- 不省略列名，若 price 可为空，则 price 为 null，否则失败</span><br><span class="line">insert into goods (id, goods_name)</span><br><span class="line">	values(20, &#x27;电脑&#x27;);</span><br><span class="line">-- 省略列名，若 price 有默认值，则可如下添加，price 为默认值，否则失败</span><br><span class="line">insert into goods</span><br><span class="line">	values(20, &#x27;电脑&#x27;);</span><br></pre></td></tr></table></figure>
</li>
<li><p>update：<code>update table_name set col_name1=expr1, col_name2=expr2, ... [where ...] </code></p>
<ul>
<li>可更新表中各列，可同时更新多个列</li>
<li>如没有 where 语句限定则修改所有行</li>
</ul>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">-- 1.将所有员工薪水修改为 5000 元（没有 where，慎用）</span><br><span class="line">update employee set salary=5000;</span><br><span class="line"></span><br><span class="line">-- 2.将姓名为 zcm 的员工薪水修改为 20000 元</span><br><span class="line">update employee </span><br><span class="line">	set salary = 20000 </span><br><span class="line">	where user_name = &#x27;zcm&#x27;;</span><br><span class="line">	</span><br><span class="line">-- 3.将姓名为 zcm 的员工薪水增加 10000 元，工作改为码农</span><br><span class="line">update employee</span><br><span class="line">	set salary = salary + 10000, job = &#x27;码农&#x27;</span><br><span class="line">	where user_name = &#x27;zcm&#x27;;</span><br></pre></td></tr></table></figure>
</li>
<li><p>delete：<code>delete from table_name [where ...]</code></p>
<ul>
<li>delete 不能用于删除某一列内容；可用 update 将该列记录全置为 null</li>
<li>delete 仅能删除记录，不能删除表本身（用 <code>drop table table_name</code>）或某一列（用 <code>alter table table_name drop column</code>）</li>
</ul>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">-- 删除姓名为 zcm 的员工记录</span><br><span class="line">delete from employee</span><br><span class="line">	where user_name = &#x27;zcm&#x27;;</span><br><span class="line">	</span><br><span class="line">-- 删除所有记录</span><br><span class="line">delete from employee;</span><br></pre></td></tr></table></figure>
</li>
<li><p><strong>select 重点（单表操作）</strong>：</p>
<p><code>select 所需查询字段，*表全部 from table_name where 查询条件</code></p>
<ul>
<li><p>关键字</p>
<ul>
<li><p>distinct：去重（要查询的记录中每个字段都相同时才去重）<code>select distinct ...</code></p>
</li>
<li><p>order by：排序。往往放最后 <code>select ... from table_name order by column asc|desc</code></p>
<p><code>asc</code>：升序（默认）；<code>desc</code>：降序</p>
<p><code>column</code> 可以是别名</p>
</li>
<li><p>as：起别名。<code>select column_name as 别名 from table_name</code></p>
</li>
<li><p>group by：分组。<code>select column1,... from table_name group by column</code></p>
</li>
<li><p>having：结合分组使用，对分组后结果进行过滤，<code>select column1,... from table_name group by column having ...</code></p>
</li>
</ul>
</li>
<li><p>合计 &#x2F; 统计函数</p>
<ul>
<li><p>count：返回行的总数。<code>select count(*|column) from table_name</code></p>
<p><code>count(*)</code> 返回满足条件的记录个数</p>
<p><code>count(column)</code> 统计某列中满足条件的有多少个，但是会排除 null</p>
</li>
<li><p>sum：求和，一般用于数值列 <code>select sum(column) from table_name</code></p>
</li>
<li><p>avg：平均值，一般用于数值列 <code>select avg(column) from table_name</code></p>
</li>
<li><p>max：最大值，一般用于数值列。<code>max(column)</code></p>
</li>
<li><p>min：最小值，一般用于数值列。<code>min(column)</code></p>
</li>
</ul>
</li>
<li><p>使用表达式对查询的列进行运算</p>
<ul>
<li><p>比较运算符：</p>
<ul>
<li>大(等)于、小(等)于、等于、不等于（<code>&lt;&gt;</code> 或 <code>!=</code>）</li>
<li>between low and high ：某一区间（low &lt;&#x3D; x &lt;&#x3D; high）</li>
<li>in(set)：在某一集合中。如 <code>in(&#39;boy&#39;, &#39;girl&#39;)</code></li>
<li>(not) like ‘pattern’：模糊查询，（不）像…</li>
<li>is null：判断是否为空</li>
</ul>
</li>
<li><p>逻辑运算：与或非</p>
</li>
</ul>
</li>
<li><p>在 select 语句中使用 as 关键字起别名</p>
</li>
</ul>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">create table student(</span><br><span class="line">	id int not null default 1,</span><br><span class="line">	name varchar(20) not null default &#x27;&#x27;,</span><br><span class="line">	chinese float not null default 0.0,</span><br><span class="line">	english float not null default 0.0,</span><br><span class="line">	math float not null default 0.0</span><br><span class="line">);</span><br><span class="line"></span><br><span class="line">insert into student(id,name,chinese,english,math) values(1,&#x27;韩顺平&#x27;,89,78,90);</span><br><span class="line">insert into student(id,name,chinese,english,math) values(2,&#x27;张飞&#x27;,67,98,56);</span><br><span class="line">insert into student(id,name,chinese,english,math) values(3,&#x27;宋江&#x27;,87,78,77);</span><br><span class="line">insert into student(id,name,chinese,english,math) values(4,&#x27;关羽&#x27;,88,98,90);</span><br><span class="line">insert into student(id,name,chinese,english,math) values(5,&#x27;赵云&#x27;,82,84,67);</span><br><span class="line">insert into student(id,name,chinese,english,math) values(6,&#x27;欧阳锋&#x27;,55,85,45);</span><br><span class="line">insert into student(id,name,chinese,english,math) values(7,&#x27;黄蓉&#x27;,75,65,30);</span><br><span class="line">insert into student(id,name,chinese,english,math) values(8,&#x27;韩信&#x27;,45,65,99);</span><br><span class="line"></span><br><span class="line">+---------+-------------+------+-----+---------+-------+</span><br><span class="line">| Field   | Type        | Null | Key | Default | Extra |</span><br><span class="line">+---------+-------------+------+-----+---------+-------+</span><br><span class="line">| id      | int(11)     | NO   |     | 1       |       |</span><br><span class="line">| name    | varchar(20) | NO   |     |         |       |</span><br><span class="line">| chinese | float       | NO   |     | 0       |       |</span><br><span class="line">| english | float       | NO   |     | 0       |       |</span><br><span class="line">| math    | float       | NO   |     | 0       |       |</span><br><span class="line">+---------+-------------+------+-----+---------+-------+</span><br><span class="line"></span><br><span class="line">+----+--------+---------+---------+------+</span><br><span class="line">| id | name   | chinese | english | math |</span><br><span class="line">+----+--------+---------+---------+------+</span><br><span class="line">|  1 | 韩顺平 |      89 |      78 |   90 |</span><br><span class="line">|  2 | 张飞   |      67 |      98 |   56 |</span><br><span class="line">|  3 | 宋江   |      87 |      78 |   77 |</span><br><span class="line">|  4 | 关羽   |      88 |      98 |   90 |</span><br><span class="line">|  5 | 赵云   |      82 |      84 |   67 |</span><br><span class="line">|  6 | 欧阳锋 |      55 |      85 |   45 |</span><br><span class="line">|  7 | 黄蓉   |      75 |      65 |   30 |</span><br><span class="line">|  8 | 韩信   |      45 |      65 |   99 |</span><br><span class="line">+----+--------+---------+---------+------+</span><br><span class="line"></span><br><span class="line">-- 查询表中所有学生的信息</span><br><span class="line">select * from student;</span><br><span class="line"></span><br><span class="line">-- 查询表中所有学生的姓名和对应的英语成绩</span><br><span class="line">select name, english from student;</span><br><span class="line"></span><br><span class="line">-- 过滤表中重复数据</span><br><span class="line">-- 要查询的记录中每个字段都相同时才去重</span><br><span class="line">-- 如下则英语成绩相同即去重，只有一个 78</span><br><span class="line">select distinct english from student;</span><br><span class="line"></span><br><span class="line">-- 统计每个学生的总分</span><br><span class="line">select name as &quot;名字&quot;, (chinese + english + math) as total_score from student;</span><br><span class="line">-- as 可省略，与上结果同</span><br><span class="line">select name &quot;名字&quot;, (chinese + english + math) total_score from student;</span><br><span class="line"></span><br><span class="line">-- 查询姓名为赵云的学生成绩</span><br><span class="line">select * from student</span><br><span class="line">	where name = &#x27;赵云&#x27;;</span><br><span class="line">-- 查询英语成绩大于 90 分的同学</span><br><span class="line">select * from student</span><br><span class="line">	where english &gt; 90;</span><br><span class="line">-- 查询总分大于 200 分的同学</span><br><span class="line">select * from student</span><br><span class="line">	where (chinese + english + math) &gt; 200;</span><br><span class="line">	</span><br><span class="line">-- 查询数学大于 60 分且 id 大于 4 的学生</span><br><span class="line">select * from student</span><br><span class="line">	where math &gt; 60 and id &gt; 4;</span><br><span class="line">-- 查询英语成绩大于语文成绩的学生</span><br><span class="line">select * from student</span><br><span class="line">	where english &gt; chinese;</span><br><span class="line">-- 查询总分大于 200 分，且数学成绩小于语文成绩的姓韩的学生</span><br><span class="line">-- 宋%，表名字以宋开头的同学，% 表 0~n 个字符</span><br><span class="line">select * from student</span><br><span class="line">	where (chinese + math + english) &gt; 200 and</span><br><span class="line">		math &lt; chinese and</span><br><span class="line">		name like &quot;宋%&quot;;</span><br><span class="line">		</span><br><span class="line">-- 英语分数在 80-90 之间的同学</span><br><span class="line">select * from student</span><br><span class="line">	where english between 80 and 90;</span><br><span class="line">-- 数学分数为 89、90、91 的同学</span><br><span class="line">select * from student</span><br><span class="line">	where math in(89,90,91);</span><br><span class="line">-- 数学大于 40 分或语文不小于 60 分的同学</span><br><span class="line">select * from student</span><br><span class="line">	where math &gt; 40 and not(chinese &lt; 60);</span><br><span class="line">-- 数学比语文少 30 分的同学</span><br><span class="line">select * from student</span><br><span class="line">	where chinese - math &gt; 30;</span><br><span class="line">	</span><br><span class="line">-- 对数学成绩升序输出</span><br><span class="line">select * from student</span><br><span class="line">	order by math asc;</span><br><span class="line">-- 对总分降序输出(column 可以是别名)</span><br><span class="line">select name, (chinese + math + english) as total from student</span><br><span class="line">	order by total desc;</span><br><span class="line">-- 对姓韩的同学根据数学成绩降序输出</span><br><span class="line">select * from student</span><br><span class="line">	where name like &quot;韩%&quot;</span><br><span class="line">	order by math desc;</span><br><span class="line">	</span><br><span class="line">-- 统计班级人数</span><br><span class="line">select count(*) from student;</span><br><span class="line">-- 返回非 null id 的个数</span><br><span class="line">select count(id) from student;</span><br><span class="line">-- 数学成绩大于等于 90 分的人数</span><br><span class="line">select count(*) from student</span><br><span class="line">	where math &gt;= 90;</span><br><span class="line">-- 总分大于 250 分的人数</span><br><span class="line">select count(*) from student</span><br><span class="line">	where (chinese + math + english) &gt; 250;</span><br><span class="line"></span><br><span class="line">-- 统计一个班级的数学总分</span><br><span class="line">select sum(math) from student;</span><br><span class="line">-- 统计一个班各科总分</span><br><span class="line">select sum(chinese) as chinese_total, sum(math) as math_total, sum(english) as english_total from student;</span><br><span class="line">-- 统计一个班各科成绩之和的总分</span><br><span class="line">select sum(chinese + math + english) as total from student;</span><br><span class="line">-- 统计一个班语文平均分</span><br><span class="line">select avg(chinese) as chinese_avg from student;</span><br><span class="line">-- 统计一个班总分平均分</span><br><span class="line">select avg(chinese + math + english) as total_avg from student;</span><br><span class="line">-- 求班级总分最高分和最低分</span><br><span class="line">select max(chinese + math + english) as top, min(chinese + math + english) as bottom from student;</span><br></pre></td></tr></table></figure>

<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line"># 部门表</span><br><span class="line">CREATE TABLE dept ( </span><br><span class="line">    deptno MEDIUMINT   UNSIGNED  NOT NULL  DEFAULT 0,</span><br><span class="line">    dname VARCHAR(20)  NOT NULL  DEFAULT &quot;&quot;,</span><br><span class="line">    loc VARCHAR(13) NOT NULL DEFAULT &quot;&quot;</span><br><span class="line">);</span><br><span class="line"># 测试数据</span><br><span class="line">insert into dept values(10,&#x27;ACCOUNTING&#x27;,&#x27;NEW YORK&#x27;);</span><br><span class="line">insert into dept values(20,&#x27;RESERACH&#x27;,&#x27;DALLAS&#x27;);</span><br><span class="line">insert into dept values(30,&#x27;SALES&#x27;,&#x27;CHICAGO&#x27;);</span><br><span class="line">insert into dept values(40,&#x27;OPERATIONS&#x27;,&#x27;BOSTON&#x27;);</span><br><span class="line"></span><br><span class="line"># 创建 EMP 雇员表</span><br><span class="line">CREATE TABLE emp (</span><br><span class="line">    empno  MEDIUMINT UNSIGNED  NOT NULL  DEFAULT 0, 	/*编号*/</span><br><span class="line">    ename VARCHAR(20) NOT NULL DEFAULT &quot;&quot;, 				/*名字*/</span><br><span class="line">    job VARCHAR(9) NOT NULL DEFAULT &quot;&quot;,					/*工作*/</span><br><span class="line">    mgr MEDIUMINT UNSIGNED,								/*上级编号*/</span><br><span class="line">    hiredate DATE NOT NULL,								/*入职时间*/</span><br><span class="line">    sal DECIMAL(7,2)  NOT NULL,							/*薪水*/</span><br><span class="line">    comm DECIMAL(7,2) ,									/*红利*/</span><br><span class="line">    deptno MEDIUMINT UNSIGNED NOT NULL DEFAULT 0 		/*部门编号*/</span><br><span class="line">);</span><br><span class="line"># 测试数据</span><br><span class="line">INSERT INTO emp VALUES(7369,&#x27;SMITH&#x27;,&#x27;CLERK&#x27;,7902,&#x27;1990-12-17&#x27;,800.00,NULL,20),(7499,&#x27;ALLEN&#x27;, &#x27;SALESMAN&#x27;,7698,&#x27;1991-2-20&#x27;,1600.00,300.00,30),</span><br><span class="line">(7521,&#x27;WARD&#x27;,&#x27;SALESMAN&#x27;,7698,&#x27;1991-2-22&#x27;,1250.00,500.00,30),</span><br><span class="line">(7566,&#x27;JONES&#x27;,&#x27;MANAGER&#x27;,7839,&#x27;1991-4-2&#x27;,2975.00,NULL,20),</span><br><span class="line">(7654,&#x27;MARTIN&#x27;,&#x27;SALESMAN&#x27;,7698,&#x27;1991-8-28&#x27;,1250.00,1400.00,30),</span><br><span class="line">(7698,&#x27;BLAKE&#x27;,&#x27;MANAGER&#x27;,7839,&#x27;1991-5-1&#x27;,2850.00,NULL,30),</span><br><span class="line">(7782,&#x27;CLARK&#x27;,&#x27;MANAGER&#x27;,7839,&#x27;1991-6-9&#x27;,2450.00,NULL,10),</span><br><span class="line">(7788,&#x27;SCOTT&#x27;,&#x27;ANALYST&#x27;,7566,&#x27;1997-4-19&#x27;,3000.00,NULL,20),</span><br><span class="line">(7839,&#x27;KING&#x27;,&#x27;PRESIDENT&#x27;,NULL,&#x27;1991-11-17&#x27;,5000.00,NULL,10),</span><br><span class="line">(7844,&#x27;TURNER&#x27;,&#x27;SALESMAN&#x27;,7698,&#x27;1991-9-8&#x27;,1500.00,NULL,30),</span><br><span class="line">(7900,&#x27;JAMES&#x27;,&#x27;CLERK&#x27;,7698,&#x27;1991-12-3&#x27;,950.00,NULL,30),</span><br><span class="line">(7902, &#x27;FORD&#x27;,&#x27;ANALYST&#x27;,7566,&#x27;1991-12-3&#x27;,3000.00,NULL,20), (7934,&#x27;MILLER&#x27;,&#x27;CLERK&#x27;,7782,&#x27;1992-1-23&#x27;, 1300.00,NULL,10);</span><br><span class="line"></span><br><span class="line"># 工资级别表</span><br><span class="line">CREATE TABLE salgrade (</span><br><span class="line">    grade MEDIUMINT UNSIGNED NOT NULL DEFAULT 0,</span><br><span class="line">    losal DECIMAL(17,2)  NOT NULL,</span><br><span class="line">    hisal DECIMAL(17,2)  NOT NULL</span><br><span class="line">);</span><br><span class="line"># 测试数据</span><br><span class="line">INSERT INTO salgrade VALUES (1,700,1200);</span><br><span class="line">INSERT INTO salgrade VALUES (2,1201,1400);</span><br><span class="line">INSERT INTO salgrade VALUES (3,1401,2000);</span><br><span class="line">INSERT INTO salgrade VALUES (4,2001,3000);</span><br><span class="line">INSERT INTO salgrade VALUES (5,3001,9999);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">-- 每个部门的平均工资和最高工资</span><br><span class="line">select avg(sal), max(sal), deptno from emp</span><br><span class="line">	group by deptno;</span><br><span class="line">-- 化繁为简：</span><br><span class="line">-- 1.每个部门的平均工资和最低工资</span><br><span class="line">-- 2.每个部门的每种岗位的平均工资和最低工资</span><br><span class="line">select avg(sal), min(sal), deptno, job from emp</span><br><span class="line">	group by deptno, job;</span><br><span class="line">-- 化繁为简：</span><br><span class="line">-- 1.每个部门的平均工资和部门号</span><br><span class="line">-- 2.平均工资低于 2000 的部门号和它的平均工资</span><br><span class="line">select avg(sal) as avg_sal, deptno from emp</span><br><span class="line">	group by deptno </span><br><span class="line">	having avg_sal &lt; 2000;</span><br></pre></td></tr></table></figure></li>
</ul>
</li>
<li><p><a target="_blank" rel="noopener" href="https://www.runoob.com/mysql/mysql-functions.html">MySQL 函数</a></p>
<ul>
<li><p>字符串函数</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">-- 1.CHARSET(str) 返回字串字符集</span><br><span class="line">select charset(ename) from emp;</span><br><span class="line"></span><br><span class="line">-- 2.CONCAT (string1 [,... ]) 连接字串, 将多个列拼接成一列</span><br><span class="line">select concat(ename, &quot; 工作是 &quot;, job) from emp;</span><br><span class="line"></span><br><span class="line">-- 3.INSTR (string, substring) 返回 substring 在 string 中出现的位置,没有返回 0</span><br><span class="line">-- dual 是一个亚元表，可作为一个测试表使用</span><br><span class="line">select instr(&quot;hanshunping&quot;, &#x27;ping&#x27;) from dual;</span><br><span class="line"></span><br><span class="line">-- 5.UCASE (string) 转换成大写</span><br><span class="line">select ucase(ename) from emp;</span><br><span class="line">-- 6.LCASE (string) 转换成小写</span><br><span class="line">select lcase(ename) from emp;</span><br><span class="line"></span><br><span class="line">-- 7.LEFT (string, length)从 string2 中的左边起取 length 个字符</span><br><span class="line">select left(ename, 2) from emp;</span><br><span class="line">-- 8.RIGHT (string,length) 从 string2 中的右边起取 length 个字符</span><br><span class="line">select right(ename, 2) from emp;</span><br><span class="line"></span><br><span class="line">-- 9.LENGTH (string)string 长度[按照字节]</span><br><span class="line">select length(ename) from emp;</span><br><span class="line"></span><br><span class="line">-- 10.REPLACE(string, search_str, replace_str)</span><br><span class="line">-- 如果是 MANAGER 则替换成 经理</span><br><span class="line">select ename, replace(job, &#x27;MANAGER&#x27;, &#x27;经理&#x27;) from emp;</span><br><span class="line"></span><br><span class="line">-- 11.STRCMP (string1 ,string2) 逐字符比较两字串大小</span><br><span class="line">select strcmp(&#x27;hsp&#x27;, &#x27;zcm&#x27;) from dual;</span><br><span class="line"></span><br><span class="line">-- 12.SUBSTRING (str , position[,length])</span><br><span class="line">-- 从 str 的 position 开始【从 1 开始计算】,取 length 个字符,省略 length 则取剩余全部</span><br><span class="line">-- 从 ename 列的第一个位置开始取出 2 个字符</span><br><span class="line">select substring(ename, 1, 2) from emp;</span><br><span class="line"></span><br><span class="line">-- 13.LTRIM (string); RTRIM (string); TRIM(string)</span><br><span class="line">-- 去除前端空格或后端空格或双端空格</span><br><span class="line">select ltrim(&quot; zcm &quot;) from dual; </span><br><span class="line">select rtrim(&quot; zcm &quot;) from dual; </span><br><span class="line">select trim(&quot; zcm &quot;) from dual; </span><br><span class="line"></span><br><span class="line">-- 以首字母大写形式显示所有员工姓名</span><br><span class="line">-- 法1</span><br><span class="line">select concat(substring(ucase(ename), 1, 1), substring(lcase(ename), 2)) as name from emp;</span><br><span class="line">-- 法2</span><br><span class="line">select concat(left(ename, 1), substring(lcase(ename), 2)) as name from emp;</span><br></pre></td></tr></table></figure>
</li>
<li><p>数学函数</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">-- 1.ABS(num) 绝对值</span><br><span class="line">select abs(-10) from dual;</span><br><span class="line"></span><br><span class="line">-- 2.BIN (decimal_number)十进制转二进制</span><br><span class="line">select bin(10) from dual;</span><br><span class="line"></span><br><span class="line">-- 3.CEILING (number2) 向上取整, 得到比 num2 大的最小整数</span><br><span class="line">select ceiling(-1.2) from dual;	/* -1 */</span><br><span class="line"></span><br><span class="line">-- 4.FLOOR (number2) 向下取整,得到比 num2 小的最大整数</span><br><span class="line">select floor(-1.2) from dual;	/* -2 */</span><br><span class="line"></span><br><span class="line">-- 5.CONV(number2,from_base,to_base) 进制转换</span><br><span class="line">-- 下面的含义是 8 是十进制的 8, 转成 2 进制输出</span><br><span class="line">select conv(8, 10, 2) from dual;</span><br><span class="line">-- 下面的含义是 8 是 16 进制的 8, 转成 2 进制输出</span><br><span class="line">select conv(8, 16, 2) from dual;</span><br><span class="line"></span><br><span class="line">-- 6.FORMAT (number,decimal_places) 保留 decimal_places 位小数(四舍五入)</span><br><span class="line">select format(78.123456, 3) from dual;			/* 78.123 */</span><br><span class="line">select format(78.123456, 4) from dual;			/* 78.1235 */</span><br><span class="line"></span><br><span class="line">-- 7.HEX (DecimalNumber) 转十六进制</span><br><span class="line">select hex(12) from dual;</span><br><span class="line"></span><br><span class="line">-- 8.LEAST(number, number2 [,..]) 求最小值</span><br><span class="line">select least(0,1,-10,4) from dual;</span><br><span class="line">-- 9.GREATEST(number, number2 [,..]) 求最大值</span><br><span class="line">select greatest(0,1,-10,4) from dual;</span><br><span class="line"></span><br><span class="line">-- 10.MOD (numerator, denominator) 求余 numerator % denominator</span><br><span class="line">select mod(10, 3) from dual;			/* 1 */</span><br><span class="line">select mod(-10, 3) from dual;			/* -1 */</span><br><span class="line">select mod(10, -3) from dual;			/* 1 */</span><br><span class="line">select mod(-10, -3) from dual;			/* -1 */</span><br><span class="line"></span><br><span class="line">-- 11.RAND([seed]) 返回随机数 其范围为 0 ≤ v ≤ 1.0</span><br><span class="line">-- 1. 如果使用 rand() 每次返回不同的随机数 ，在 0 ≤ v ≤ 1.0</span><br><span class="line">-- 2. 如果使用 rand(seed) 返回随机数, 范围 0 ≤ v ≤ 1.0, 如果 seed 不变，该随机数也不变</span><br><span class="line">select rand() from dual;</span><br><span class="line">select rand(17) from dual;</span><br></pre></td></tr></table></figure>
</li>
<li><p>日期函数</p>
<p>timestamp 基本同 now</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">-- 1.CURRENT_DATE() 当前日期</span><br><span class="line">select current_date() from dual;</span><br><span class="line">-- 2.CURRENT_TIME() 当前时间</span><br><span class="line">select current_time() from dual;</span><br><span class="line">-- 3.CURRENT_TIMESTAMP() 当前时间戳</span><br><span class="line">select current_timestamp() from dual;</span><br><span class="line">-- 4.NOW() 当前时间</span><br><span class="line">select now() from dual;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">CREATE TABLE mes(</span><br><span class="line">	id INT ,</span><br><span class="line">	content VARCHAR(30), </span><br><span class="line">    send_time DATETIME);</span><br><span class="line">    </span><br><span class="line">INSERT INTO mes VALUES(1, &#x27;北京新闻&#x27;, CURRENT_TIMESTAMP());</span><br><span class="line">INSERT INTO mes VALUES(2, &#x27;上海新闻&#x27;, NOW());</span><br><span class="line">INSERT INTO mes VALUES(3, &#x27;广州新闻&#x27;, NOW());</span><br><span class="line"></span><br><span class="line">-- 5.DATE(datetime) 返货 datetime 中的日期</span><br><span class="line">-- 显示所有新闻信息，发布日期只显示 日期，不用显示时间</span><br><span class="line">select id, content, date(send_time) from mes;</span><br><span class="line"></span><br><span class="line">-- 6.DATE_ADD(date2, INTERVAL d_value d_type) 在 date2 中加上日期或时间</span><br><span class="line">-- 7.DATE_SUB(date2, INTERVAL d_value d_type) 在 date2 中减去日期或时间</span><br><span class="line">-- INTERVAL 后面的单位可以是 year、month、day、hour、minute、second 等</span><br><span class="line">-- 前 - 后；可能为负数</span><br><span class="line">-- 请查询在 10 分钟内发布的新闻, 思路一定要梳理一下</span><br><span class="line">select * from mes</span><br><span class="line">	where date_add(send_time, interval 10 minute) &gt;= now();</span><br><span class="line">-- 或</span><br><span class="line">select * from mes</span><br><span class="line">	where date_sub(now(), interval 10 minute) &lt;= send_time;</span><br><span class="line">	</span><br><span class="line">-- 8.DATEDIFF(date1, date2) 两个日期差</span><br><span class="line">-- 请在 mysql 的 sql 语句中求出 2011-11-11 和 1990-1-1 相差多少天</span><br><span class="line">select datediff(&#x27;2011-11-11&#x27;, &#x27;1990-01-01&#x27;) from dual;</span><br><span class="line">-- 请用 mysql 的 sql 语句求出你活了多少天</span><br><span class="line">select datediff(now(), &#x27;2000-06-08&#x27;) from dual;</span><br><span class="line">-- 如果你能活 80 岁，求出你还能活多少天</span><br><span class="line">select datediff(date_add(&#x27;2000-06-08&#x27;, interval 80 year), now()) from dual;</span><br><span class="line"></span><br><span class="line">-- 5.6.7.8 四个函数的日期类型可以是 date、datetime、datestamp</span><br><span class="line"></span><br><span class="line">-- 9.TIMEDIFF(time1, time2) 两个时间差</span><br><span class="line">select timediff(&#x27;10:11:11&#x27;, &#x27;06:10:10&#x27;) from dual;</span><br><span class="line"></span><br><span class="line">-- 10.YEAR|MONTH|DAY|DATE|HOUR|MINUTE|SECOND|TIME(datetime) 取出对应部分</span><br><span class="line">select year(&#x27;2018-03-17 18:30:25&#x27;) from dual;</span><br><span class="line">select month(&#x27;2018-03-17 18:30:25&#x27;) from dual;</span><br><span class="line">select day(&#x27;2018-03-17 18:30:25&#x27;) from dual;</span><br><span class="line">select date(&#x27;2018-03-17 18:30:25&#x27;) from dual;</span><br><span class="line">select hour(now()) from dual;</span><br><span class="line">select minute(now()) from dual;</span><br><span class="line">select second(now()) from dual;</span><br><span class="line">select time(now()) from dual;</span><br><span class="line"></span><br><span class="line">-- 11.UNIX_TIMESTAMP() 返回 1970-1-1 至今的秒数</span><br><span class="line">select unix_timestamp() from dual;</span><br><span class="line"></span><br><span class="line">-- 12.FROM_UNIXTIME() : 可以把一个 unix_timestamp 秒数[时间戳]，转成指定格式的日期</span><br><span class="line">-- %Y-%m-%d 格式是规定好的，表示年月日</span><br><span class="line">-- 意义：在开发中，可以存放一个整数，然后表示时间，通过 FROM_UNIXTIME 转换</span><br><span class="line">select from_unixtime(1644319971, &#x27;%Y-%m-%d&#x27;) from dual;</span><br><span class="line">select from_unixtime(1644319971, &#x27;%Y-%m-%d %H:%i:%s&#x27;) from dual;</span><br></pre></td></tr></table></figure>
</li>
<li><p>加密函数和系统函数</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">-- 1.USER() 查询用户</span><br><span class="line">-- 可以查看登录到 mysql 的有哪些用户，以及登录的 IP</span><br><span class="line">-- 用户 @IP 地址</span><br><span class="line">select user() from dual; </span><br><span class="line"></span><br><span class="line">-- 2.DATABASE() 查询当前使用数据库名称</span><br><span class="line">select database();</span><br><span class="line"></span><br><span class="line">-- 3.MD5(str) 为字符串算出一个 MD5 32 的字符串，常用(用户密码)加密</span><br><span class="line">-- root 密码是 zcm -&gt; 加密 md5 -&gt; 在数据库中存放的是加密后的密码</span><br><span class="line">select md5(&#x27;zcm&#x27;) from dual;</span><br><span class="line"></span><br><span class="line">-- 4.PASSWORD(str) -- 加密函数, MySQL 数据库的用户密码就是 PASSWORD 函数加密</span><br><span class="line">-- 使用 MySQLSHA1（安全Hash算法）进行加密</span><br><span class="line">select password(&#x27;zcm&#x27;) from dual;</span><br><span class="line"></span><br><span class="line">-- 5.select * from mysql.user 从原文密码 str 计算并返回密码字符串</span><br><span class="line">-- 通常用于对 mysql 数据库的用户密码加密</span><br><span class="line">-- mysql.user 表示 数据库.表</span><br><span class="line">select * from mysql.user;</span><br></pre></td></tr></table></figure>
</li>
<li><p>流程控制函数</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line"># 1.IF(expr1,expr2,expr3) 如果 expr1 为 True ,则返回 expr2 否则返回 expr3</span><br><span class="line">select if(true, &#x27;北京&#x27;, &#x27;上海&#x27;) from dual;</span><br><span class="line"></span><br><span class="line"># 2.IFNULL(expr1,expr2) 如果 expr1 不为空 NULL,则返回 expr1,否则返回 expr2</span><br><span class="line">select ifnull(null, &#x27;zcm&#x27;) from dual;</span><br><span class="line"></span><br><span class="line"># 3.SELECT CASE [WHEN expr1 THEN expr2] [WHEN expr3 THEN expr4] ELSE expr5 END; </span><br><span class="line">select case</span><br><span class="line">	when expr1 then expr2</span><br><span class="line">	when expr3 then expr4</span><br><span class="line">	else expr5 end;</span><br><span class="line"># 如果 expr1 为 TRUE,则返回 expr2,如果 expr2 为 TRUE, 返回 expr4, 否则返回 expr5</span><br><span class="line"># [类似多重分支，括号中可重复多次]</span><br><span class="line">select case </span><br><span class="line">	when true then &#x27;111&#x27; </span><br><span class="line">	when false then &#x27;222&#x27; </span><br><span class="line">	else &#x27;333&#x27; end;</span><br><span class="line">	</span><br><span class="line">-- 查询 emp 表, 如果 comm 是 null , 则显示 0.0</span><br><span class="line">-- 老师说明，判断是否为 null 要使用 is null, 判断不为空 使用 is not</span><br><span class="line">select ename, if(comm is null, 0.0, comm) from emp;</span><br><span class="line">-- 或</span><br><span class="line">select ename ifnull(comm, 0.0) from emp;</span><br><span class="line"></span><br><span class="line">-- 如果 emp 表的 job 是 CLERK 则显示 职员， 如果是 MANAGER 则显示经理</span><br><span class="line">-- 如果是 SALESMAN 则显示 销售人员，其它正常显示</span><br><span class="line">select ename, (select case</span><br><span class="line">                when job=&#x27;CLERK&#x27; then &#x27;职员&#x27;</span><br><span class="line">                when job=&#x27;MANAGER&#x27; then &#x27;经理&#x27;</span><br><span class="line">                when job=&#x27;SALESMAN&#x27; then &#x27;销售人员&#x27;</span><br><span class="line">                else job end) as cn_job, job</span><br><span class="line">	from emp;</span><br></pre></td></tr></table></figure></li>
</ul>
</li>
<li><p>查询增强</p>
<ul>
<li>判断是否为 null 要使用 <code>column is null</code>, 判断不为空使用 <code>column is not null</code></li>
</ul>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line"># 1.where 增强</span><br><span class="line">-- 查找 1992.1.1 后入职的员工</span><br><span class="line">-- 在 mysql 中,日期类型可以直接比较,需要注意进行比较的格式一致</span><br><span class="line">select * from emp</span><br><span class="line">	where hiredate &gt; &#x27;1992.01.01&#x27;;</span><br><span class="line"></span><br><span class="line"># 2.like 增强</span><br><span class="line">-- 使用 like 操作符(模糊查询)</span><br><span class="line">-- %: 表示 0 到多个任意字符 </span><br><span class="line">-- _: 表示单个任意字符</span><br><span class="line">-- 显示首字符为 S 的员工姓名和工资</span><br><span class="line">select ename, sal from emp</span><br><span class="line">	where ename like &#x27;S%&#x27;;</span><br><span class="line">-- 显示第三个字符为大写 O 的所有员工的姓名和工资</span><br><span class="line">select ename, sal from emp</span><br><span class="line">	where ename like &#x27;__O%&#x27;;</span><br><span class="line">-- 显示没有上级 (mgr is null) 的雇员的情况</span><br><span class="line">select * from emp</span><br><span class="line">	where mgr is null;</span><br><span class="line">-- 错误写法，不能用 =</span><br><span class="line">select * from emp</span><br><span class="line">	where mgr = null;</span><br><span class="line"></span><br><span class="line"># 3.order by 增强</span><br><span class="line">-- 按照部门号升序而雇员的工资降序排列,显示雇员信息</span><br><span class="line">-- 表示先按 deptno 升序排序，deptno 相等时按 sal 降序排序</span><br><span class="line">select * from emp</span><br><span class="line">	order by deptno asc, sal desc;</span><br><span class="line">-- 注意按xx排序的顺序不能换</span><br><span class="line">-- 表示先按 sal 降序排序，sal 相等时按 deptno 升序排序</span><br><span class="line">select * from emp</span><br><span class="line">	order by sal desc, deptno asc;</span><br><span class="line">	</span><br><span class="line"># 4.分页查询 [limit start, rows] 表示从 start + 1 行开始取，共取出 rows 行（start 从 0 开始）</span><br><span class="line">-- 按雇员的 empno 号升序取出，每页显示 3 条记录，请分别显示 第 1 页，第 2 页，第 3 页</span><br><span class="line">select * from emp		# 第一页</span><br><span class="line">	order by empno asc</span><br><span class="line">	limit 0, 3;</span><br><span class="line">select * from emp		# 第二页</span><br><span class="line">	order by empno asc</span><br><span class="line">	limit 3, 3;</span><br><span class="line">select * from emp		# 第三页</span><br><span class="line">	order by empno asc</span><br><span class="line">	limit 6, 3;</span><br><span class="line">-- 推导公式如下</span><br><span class="line">select * from emp		</span><br><span class="line">	order by empno asc</span><br><span class="line">	limit 每页显示记录数 * (第 n 页 - 1) , 每页显示记录数</span><br><span class="line"></span><br><span class="line"># 5.group by 增强</span><br><span class="line">-- 显示每种岗位的雇员总数、平均工资</span><br><span class="line">select job, count(*), avg(sal) from emp</span><br><span class="line">	group by job;</span><br><span class="line">-- 显示雇员总数，以及获得补助(comm is not null)的雇员数</span><br><span class="line">select count(*) from emp</span><br><span class="line">	where comm is not null;</span><br><span class="line">-- 或直接（count 不会统计 null）</span><br><span class="line">select count(comm) from emp;</span><br><span class="line">-- 显示管理者(mgr)的总人数。小技巧: 尝试写-&gt;修改-&gt;尝试[正确的]</span><br><span class="line">select count(distinct mgr) from emp;</span><br><span class="line">-- 显示雇员工资的最大差额</span><br><span class="line">select max(sal)-min(sal) as diff from emp;</span><br></pre></td></tr></table></figure>

<ul>
<li><p>多个关键字的顺序（顺序错误会报错）</p>
<p><strong>from -&gt; on -&gt; join(各种join) -&gt; where -&gt; group by -&gt; having -&gt; select -&gt; distinct -&gt; union -&gt; order by -&gt; limit</strong></p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">select column1, column2, ... from table_name</span><br><span class="line">	where ...</span><br><span class="line">	group by column</span><br><span class="line">	having condition</span><br><span class="line">	order by column</span><br><span class="line">	limit start, rows;</span><br><span class="line">	</span><br><span class="line">-- 请统计各个部门中的平均工资大于 1000 的部门</span><br><span class="line">-- 并且按照平均工资从高到低排序</span><br><span class="line">-- 取出前两行记录</span><br><span class="line">select deptno, avg(sal) as avg_sal from emp</span><br><span class="line">	where sal &gt; 1000</span><br><span class="line">	group by deptno</span><br><span class="line">	having avg_sal &gt; 1000</span><br><span class="line">	order by avg_sal desc</span><br><span class="line">	limit 0, 2;</span><br></pre></td></tr></table></figure></li>
</ul>
</li>
<li><p><strong>select 重点（多表操作）</strong></p>
<ul>
<li><p>多表查询的条件不能少于 <code>表的个数-1</code>，否则会出现笛卡尔集；如 3 个表联合查询，至少需要 2 个查询条件</p>
</li>
<li><p>笛卡尔集：每条信息两两结合</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line"># emp、dept 两个表中的每条信息两两结合，共 13 * 4 条记录</span><br><span class="line">select * from emp, dept;</span><br><span class="line">select * from emp join dept;</span><br><span class="line"></span><br><span class="line">-- select * from emp join dept on [condition]</span><br><span class="line">-- select * from emp join dept where [condition]</span><br></pre></td></tr></table></figure>
</li>
<li><p><a target="_blank" rel="noopener" href="https://www.cnblogs.com/buwuliao/p/11812824.html">on 和 where 的区别</a>（只对外连接有区别）</p>
<ul>
<li>on 条件是在生成临时表时使用的条件，它不管 on 中的条件是否为真，都会返回左边表中的记录</li>
<li>where 条件是在临时表生成好后，再对临时表进行过滤的条件。这时已经没有 left join 的含义（必须返回左边表的记录）了，条件不为真的就全部过滤掉</li>
</ul>
</li>
<li><p><a target="_blank" rel="noopener" href="https://blog.csdn.net/try17875864815/article/details/104874610">关系代数运算之笛卡尔积、等值连接、自然连接、内连接的区别</a></p>
<p>使用之前创建的 <code>dept、emp、salgrade</code> 三张表</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">-- 部门表</span><br><span class="line">+--------+-----------------------+------+-----+---------+-------+</span><br><span class="line">| Field  | Type                  | Null | Key | Default | Extra |</span><br><span class="line">+--------+-----------------------+------+-----+---------+-------+</span><br><span class="line">| deptno | mediumint(8) unsigned | NO   |     | 0       |       |</span><br><span class="line">| dname  | varchar(20)           | NO   |     |         |       |</span><br><span class="line">| loc    | varchar(13)           | NO   |     |         |       |</span><br><span class="line">+--------+-----------------------+------+-----+---------+-------+</span><br><span class="line"></span><br><span class="line">-- 员工表</span><br><span class="line">+----------+-----------------------+------+-----+---------+-------+</span><br><span class="line">| Field    | Type                  | Null | Key | Default | Extra |</span><br><span class="line">+----------+-----------------------+------+-----+---------+-------+</span><br><span class="line">| empno    | mediumint(8) unsigned | NO   |     | 0       |       |</span><br><span class="line">| ename    | varchar(20)           | NO   |     |         |       |</span><br><span class="line">| job      | varchar(9)            | NO   |     |         |       |</span><br><span class="line">| mgr      | mediumint(8) unsigned | YES  |     | NULL    |       |</span><br><span class="line">| hiredate | date                  | NO   |     | NULL    |       |</span><br><span class="line">| sal      | decimal(7,2)          | NO   |     | NULL    |       |</span><br><span class="line">| comm     | decimal(7,2)          | YES  |     | NULL    |       |</span><br><span class="line">| deptno   | mediumint(8) unsigned | NO   |     | 0       |       |</span><br><span class="line">+----------+-----------------------+------+-----+---------+-------+</span><br><span class="line"></span><br><span class="line">-- 工资等级表</span><br><span class="line">+-------+-----------------------+------+-----+---------+-------+</span><br><span class="line">| Field | Type                  | Null | Key | Default | Extra |</span><br><span class="line">+-------+-----------------------+------+-----+---------+-------+</span><br><span class="line">| grade | mediumint(8) unsigned | NO   |     | 0       |       |</span><br><span class="line">| losal | decimal(17,2)         | NO   |     | NULL    |       |</span><br><span class="line">| hisal | decimal(17,2)         | NO   |     | NULL    |       |</span><br><span class="line">+-------+-----------------------+------+-----+---------+-------+</span><br><span class="line"></span><br><span class="line">-- 显示雇员名,雇员工资及所在部门的名字</span><br><span class="line">-- 法1</span><br><span class="line">select ename, sal, dname</span><br><span class="line">	from emp join dept</span><br><span class="line">	on emp.deptno=dept.deptno;</span><br><span class="line">-- 法2</span><br><span class="line">select ename, sal, dname</span><br><span class="line">	from emp, dept</span><br><span class="line">	where emp.deptno=dept.deptno;</span><br><span class="line">-- 法3</span><br><span class="line">select ename, sal, dname</span><br><span class="line">	from emp natural join dept;</span><br><span class="line">	</span><br><span class="line">-- 显示部门号为 10 的部门名、员工名和工资</span><br><span class="line">select ename, sal, dname</span><br><span class="line">	from emp join dept</span><br><span class="line">	on emp.deptno=dept.deptno and emp.deptno=10;</span><br><span class="line">	</span><br><span class="line">-- 显示各个员工的姓名，工资，及其工资的级别</span><br><span class="line">select ename, sal, grade</span><br><span class="line">	from emp join salgrade</span><br><span class="line">	on sal between losal and hisal;</span><br><span class="line">	</span><br><span class="line">-- 自连接，将同一张表看作两张表，自己和自己连接(需起别名，as 可省略)</span><br><span class="line">-- 显示公司员工名字和他的上级的名字</span><br><span class="line">select worker.ename, boss.ename </span><br><span class="line">	from emp as worker join emp as boss</span><br><span class="line">	on worker.mgr = boss.empno;</span><br></pre></td></tr></table></figure>
</li>
<li><p>子查询（嵌套查询）：嵌入在其他 <code>sql</code> 语句中的 <code>select</code> 语句</p>
<p>化繁为简，从内到外，逐层分析 </p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">-- 1.单行子查询：只返回一行数据的子查询语句</span><br><span class="line">-- 显示与 SMITH 同一部门的所有员工</span><br><span class="line">select * from emp</span><br><span class="line">	where deptno = (</span><br><span class="line">        select deptno </span><br><span class="line">        from emp</span><br><span class="line">        where ename = &#x27;SMITH&#x27;);</span><br><span class="line"></span><br><span class="line">-- 2.多行子查询：返回多行数据的子查询，使用关键字 in</span><br><span class="line">-- 查询和部门 10 的工作相同的雇员的</span><br><span class="line">-- 名字、岗位、工资、部门号, 但是不含 10 号部门自己的雇员</span><br><span class="line">select ename, job, sal, deptno</span><br><span class="line">	from emp</span><br><span class="line">	where job in(</span><br><span class="line">    	select distinct job</span><br><span class="line">        from emp</span><br><span class="line">        where deptno = 10</span><br><span class="line">    ) and deptno != 10;</span><br></pre></td></tr></table></figure>
</li>
<li><p>子查询当做临时表</p>
<p>可解决很多复杂查询</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">-- 查询各部门中工资最高的员工</span><br><span class="line">-- 因为 ename 不能用 deptno 分组，所以错误</span><br><span class="line">select ename, deptno, max(sal) as max_sal from emp</span><br><span class="line">	group by deptno;</span><br><span class="line">-- (1) 先得到各部门中工资最高的部门号和工资</span><br><span class="line">select deptno, max(sal) as max_sal </span><br><span class="line">	from emp</span><br><span class="line">	group by deptno;</span><br><span class="line">-- (2) 将 (1) 结果创建临时表 temp，然后合并查询</span><br><span class="line">-- 因为两个表都用 deptno，所以需指定表名</span><br><span class="line">select ename, emp.deptno, max_sal from (</span><br><span class="line">        select deptno, max(sal) as max_sal </span><br><span class="line">        from emp</span><br><span class="line">        group by deptno</span><br><span class="line">	) as temp join emp</span><br><span class="line">	on temp.deptno = emp.deptno</span><br><span class="line">	and temp.max_Sal = emp.sal;</span><br><span class="line">	</span><br><span class="line">-- 查找每个部门工资高于本部门平均工资的人的资料</span><br><span class="line">select * from (</span><br><span class="line">        select deptno, avg(sal) as avg_sal</span><br><span class="line">        from emp</span><br><span class="line">        group by deptno</span><br><span class="line">    ) as temp join emp</span><br><span class="line">	on temp.deptno = emp.deptno</span><br><span class="line">	and avg_sal &lt; sal;</span><br><span class="line">	</span><br><span class="line">-- 查询每个部门的信息(包括：部门名,编号,地址)和人员数量</span><br><span class="line">-- 表名.* 表示该表所有列</span><br><span class="line">select dept.*, empNum</span><br><span class="line">	from (</span><br><span class="line">    	select count(*) as empNum, deptno</span><br><span class="line">        from emp</span><br><span class="line">        group by deptno</span><br><span class="line">    ) as temp join dept</span><br><span class="line">    on temp.deptno = dept.deptno;</span><br><span class="line">	</span><br></pre></td></tr></table></figure>
</li>
<li><p>all 和 any</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">-- all,显示工资比部门 30 的所有员工的工资高的员工的姓名、工资和部门号</span><br><span class="line">select ename, sal, deptno </span><br><span class="line">	from emp</span><br><span class="line">	where sal &gt; all(</span><br><span class="line">        select sal</span><br><span class="line">        from emp</span><br><span class="line">    	where deptno = 30</span><br><span class="line">    );</span><br><span class="line">-- 或不用 all，用 max</span><br><span class="line">select ename, sal, deptno </span><br><span class="line">	from emp</span><br><span class="line">	where sal &gt; (</span><br><span class="line">        select max(sal)</span><br><span class="line">        from emp</span><br><span class="line">    	where deptno = 30</span><br><span class="line">    );</span><br><span class="line">    </span><br><span class="line">-- any,显示工资比部门 30 的其中一个员工的工资高的员工的姓名、工资和部门号</span><br><span class="line">select ename, sal, deptno </span><br><span class="line">	from emp</span><br><span class="line">	where sal &gt; any(</span><br><span class="line">        select sal</span><br><span class="line">        from emp</span><br><span class="line">    	where deptno = 30</span><br><span class="line">    );</span><br><span class="line">-- 或不用 any，用 min</span><br><span class="line">select ename, sal, deptno </span><br><span class="line">	from emp</span><br><span class="line">	where sal &gt; (</span><br><span class="line">        select min(sal)</span><br><span class="line">        from emp</span><br><span class="line">    	where deptno = 30</span><br><span class="line">    );</span><br></pre></td></tr></table></figure>
</li>
<li><p>多列子查询：子查询返回多列，有（单列单行、单列多行、多列单行、多列多行）</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">-- 查询与 ALLEN 的部门和岗位完全相同的所有雇员(并且不含 ALLEN 本人)</span><br><span class="line">-- 多列查询</span><br><span class="line">select * </span><br><span class="line">	from emp</span><br><span class="line">    where (deptno, job) = (</span><br><span class="line">    	select deptno, job</span><br><span class="line">        from emp</span><br><span class="line">        where ename = &#x27;ALLEN&#x27;</span><br><span class="line">    ) and ename != &#x27;ALLEN&#x27;;</span><br><span class="line">-- 或临时表</span><br><span class="line">select * </span><br><span class="line">	from (</span><br><span class="line">        select deptno, job</span><br><span class="line">        from emp</span><br><span class="line">        where ename = &#x27;ALLEN&#x27;</span><br><span class="line">    ) as temp join emp</span><br><span class="line">    on temp.deptno = emp.deptno</span><br><span class="line">	and temp.job = emp.job</span><br><span class="line">	and ename != &#x27;ALLEN&#x27;;</span><br></pre></td></tr></table></figure>
</li>
<li><p>表复制和去重</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">-- 1.表复制</span><br><span class="line">create table my_tab01 ( </span><br><span class="line">    id int, </span><br><span class="line">    name varchar(32), </span><br><span class="line">    sal double, </span><br><span class="line">    job varchar(32), </span><br><span class="line">    deptno int);</span><br><span class="line"></span><br><span class="line">-- 把 emp 复制到 my_tab01</span><br><span class="line">insert into my_tab01 (id, name, sal, job, deptno)</span><br><span class="line">	select empno, ename, sal, job, deptno from emp;</span><br><span class="line">-- 自我复制（自我复制数据，即蠕虫复制，指数级增长）</span><br><span class="line">insert into my_tab01</span><br><span class="line">	select * from my_tab01;</span><br><span class="line">	</span><br><span class="line"></span><br><span class="line">-- 2.表去重</span><br><span class="line">-- 复制 emp 结构到 my_tab02; desc 两者相等</span><br><span class="line">create table my_tab02 like emp;</span><br><span class="line">-- 执行两次，使其有重复记录</span><br><span class="line">insert into my_tab02</span><br><span class="line">	select * from emp;</span><br><span class="line">-- (1)创建新表，插入去重后数据</span><br><span class="line">create table remove_repeat like my_tab02;</span><br><span class="line">insert into remove_repeat</span><br><span class="line">	select distinct * from my_tab02;</span><br><span class="line">-- (2)删除原表</span><br><span class="line">drop table my_tab02;</span><br><span class="line">-- (3)修改新表表名</span><br><span class="line">rename table remove_repeat to my_tab02;</span><br><span class="line">-- 或删除原表数据；将新表数据插入原表；删除新表</span><br></pre></td></tr></table></figure>
</li>
<li><p>合并查询（union）</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">select ename, sal, job</span><br><span class="line">	from emp</span><br><span class="line">	where sal &gt; 2500;</span><br><span class="line">	</span><br><span class="line">select ename, sal, job</span><br><span class="line">	from emp</span><br><span class="line">	where job = &#x27;MANAGER&#x27;;</span><br><span class="line">	</span><br><span class="line">-- union all(不去重)</span><br><span class="line">select ename, sal, job from emp where sal &gt; 2500</span><br><span class="line">union all</span><br><span class="line">select ename, sal, job from emp where job = &#x27;MANAGER&#x27;;</span><br><span class="line">-- union(去重)</span><br><span class="line">select ename, sal, job from emp where sal &gt; 2500</span><br><span class="line">union</span><br><span class="line">select ename, sal, job from emp where job = &#x27;MANAGER&#x27;;</span><br></pre></td></tr></table></figure>
</li>
<li><p>外连接（没有内连接常用）</p>
<ul>
<li>左外连接：左侧表完全显示，如右侧没有匹配则显示空</li>
<li>右外连接：右侧表完全显示，如左侧没有匹配则显示空</li>
<li>全连接：MySQL 不支持 <code>full outer join</code>；可以<code>left join union right join</code></li>
<li>内连接：即只有 <code>join</code>，只有两边都匹配时才显示，没有匹配项时不显示</li>
</ul>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">-- 列出部门名称和这些部门的员工名称和工作</span><br><span class="line">-- 同时要求 显示出那些没有员工的部门</span><br><span class="line">select dname, ename, job</span><br><span class="line">	from dept left join emp</span><br><span class="line">	on dept.deptno = emp.deptno</span><br><span class="line">	order by dname;</span><br><span class="line"></span><br><span class="line">-- OPERATIONS 部门没有员工，也显示出来</span><br><span class="line">+------------+--------+-----------+</span><br><span class="line">| dname      | ename  | job       |</span><br><span class="line">+------------+--------+-----------+</span><br><span class="line">| ACCOUNTING | MILLER | CLERK     |</span><br><span class="line">| ACCOUNTING | CLARK  | MANAGER   |</span><br><span class="line">| ACCOUNTING | KING   | PRESIDENT |</span><br><span class="line">| OPERATIONS | NULL   | NULL      |</span><br><span class="line">| RESERACH   | SMITH  | CLERK     |</span><br><span class="line">| RESERACH   | SCOTT  | ANALYST   |</span><br><span class="line">| RESERACH   | FORD   | ANALYST   |</span><br><span class="line">| RESERACH   | JONES  | MANAGER   |</span><br><span class="line">| SALES      | WARD   | SALESMAN  |</span><br><span class="line">| SALES      | TURNER | SALESMAN  |</span><br><span class="line">| SALES      | MARTIN | SALESMAN  |</span><br><span class="line">| SALES      | ALLEN  | SALESMAN  |</span><br><span class="line">| SALES      | JAMES  | CLERK     |</span><br><span class="line">| SALES      | BLAKE  | MANAGER   |</span><br><span class="line">+------------+--------+-----------+</span><br></pre></td></tr></table></figure></li>
</ul>
</li>
<li><p>约束</p>
<p>用于确保数据库中的数据满足特定的商业规则</p>
<p>包括 <code>not null、unique、primary key、foreign key、check</code> 五种</p>
<p><code>desc table_name</code> 可显示约束情况 </p>
<ul>
<li><p><code>primary key</code> 主键：用于唯一标识该行数据，主键值不可重复；</p>
<ul>
<li><p>主键不能重复且不能为 null</p>
</li>
<li><p>一张表最多只有一个主键，但可以是复合主键</p>
</li>
<li><p>主键指定方式有两种</p>
<ul>
<li>列名后直接指定 <code>primary key</code></li>
<li>表定义最后写 <code>primary key(column)</code></li>
</ul>
</li>
<li><p>实际开发中往往每个表都有一个主键（身份证号）</p>
</li>
</ul>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">create table t1 (</span><br><span class="line">		id int primary key, 			-- 表 id 为主键</span><br><span class="line">    	name varchar(32) primary key,	-- 错误，主键只有一个</span><br><span class="line">    	email varchar(32)</span><br><span class="line">	);</span><br><span class="line"></span><br><span class="line">-- 复合主键(id + name)</span><br><span class="line">create table t1 (</span><br><span class="line">		id int,</span><br><span class="line">    	name varchar(32),</span><br><span class="line">    	email varchar(32),</span><br><span class="line">    	primary key(id, name)	-- 复合主键，id、name 都重复才不可添加</span><br><span class="line">	);</span><br></pre></td></tr></table></figure>
</li>
<li><p><code>not null</code> 不可为空</p>
<ul>
<li>一张表可有多个 not null</li>
</ul>
</li>
<li><p><code>unique</code> 该列值不可重复</p>
<ul>
<li>一张表可以有多个 unique</li>
<li>如指定 unique 的列没有指定 not null，则可有重复 null</li>
<li>如一个字段用 <code>unique not null</code> 修饰，使用效果同主键</li>
</ul>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">create table t1 (</span><br><span class="line">		id int unique,</span><br><span class="line">    	name varchar(32),</span><br><span class="line">    	email varchar(32)</span><br><span class="line">	);</span><br><span class="line"></span><br><span class="line">insert into t1 value(null, &#x27;1&#x27;, &#x27;1@qq.com&#x27;);	-- 成功</span><br><span class="line">insert into t1 value(null, &#x27;2&#x27;, &#x27;2@qq.com&#x27;);	-- 成功，可有重复 null</span><br></pre></td></tr></table></figure>
</li>
<li><p><code>foreign key</code> 外键：用于定义主表和从表之间的关系</p>
<ul>
<li><p>定义外键后，<strong>要求外键列数据必须在主表的主键列存在或为 null（前提是外键允许为 null）</strong></p>
</li>
<li><p>外键约束定义在从表上，主表则必须具有主键约束或 unique 约束</p>
</li>
<li><p>表的存储引擎为 <code>innodb</code> 才支持外键</p>
</li>
<li><p>外键与主键类型一致，长度可不同</p>
</li>
<li><p>一旦建立外键关系，则主键不能随意删除</p>
<p>如 id&#x3D;200 的班级有学生，则不能删除 id&#x3D;200 的班级，必须先将该班级的学生删除才能删除班级</p>
</li>
</ul>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">-- 先建主表</span><br><span class="line">create table my_class (</span><br><span class="line">		id int primary key,						-- 班级编号</span><br><span class="line">    	name varchar(32) not null default &#x27;&#x27;	-- 班级名称</span><br><span class="line">	);</span><br><span class="line">	</span><br><span class="line">insert into my_class values(100, &#x27;java&#x27;), (200, &#x27;web&#x27;);</span><br><span class="line">	</span><br><span class="line">-- 再建从表</span><br><span class="line">class table my_stu (</span><br><span class="line">		id int primary key,						-- 学生编号</span><br><span class="line">    	name varchar(32) not null default &#x27;&#x27;,	-- 学生名称</span><br><span class="line">    	class_id int,							-- 学生所在班级编号，即外键</span><br><span class="line">    	-- 指定外键</span><br><span class="line">    	foreign key(class_id) references my_class(id)</span><br><span class="line">	);</span><br><span class="line"></span><br><span class="line">insert into my_stu values(1, &#x27;tom&#x27;, 100);		-- 成功</span><br><span class="line">insert into my_stu values(2, &#x27;jack&#x27;, 200);		-- 成功</span><br><span class="line">insert into my_stu values(3, &#x27;try&#x27;, 300);		-- 失败，没有 id=300 的班级</span><br><span class="line">insert into my_stu values(4, &#x27;zcm&#x27;, null);		-- 成功</span><br><span class="line">delete from my_class where id = 200;			-- 失败，id=200 的班级有学生，则不能删除 id=200 的班级</span><br></pre></td></tr></table></figure>
</li>
<li><p><code>check</code> ：用于强制行数据必须满足的条件</p>
<ul>
<li><code>oracle</code> 和 <code>sql server</code> 支持 <code>check</code>，但是 <code>mysql 5.7</code> 不支持（<strong>只做语法校验，但不会生效</strong>）</li>
</ul>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">create table t1 (</span><br><span class="line">		id int,</span><br><span class="line">    	sex char(1) check(sex in(&#x27;女&#x27;, &#x27;男&#x27;)),</span><br><span class="line">    	sal double check(sal &gt; 1000 and sal &lt; 200)</span><br><span class="line">	);</span><br><span class="line">	</span><br><span class="line">-- check 不生效，可用枚举 enum</span><br><span class="line">create table t1 (</span><br><span class="line">		id int,</span><br><span class="line">    	sex enum(&#x27;女&#x27;, &#x27;男&#x27;) not null,</span><br><span class="line">    	sal double check(sal &gt; 1000 and sal &lt; 200)</span><br><span class="line">	);</span><br></pre></td></tr></table></figure>
</li>
<li><p>自增长 <code>auto_increment</code></p>
<ul>
<li>一般自增长和主键配合使用</li>
<li>自增长也可单独使用（需搭配 unique）</li>
<li>自增长一般修饰整型（小数也可，很少用）</li>
<li>自增长默认从 1 开始；可修改默认自增长值 <code>alter table table_name auto_increment = 100</code></li>
<li>自增长是该字段最大值 + 1</li>
<li>也可指定添加数据（一般不这样做）</li>
</ul>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">create table t1 (</span><br><span class="line">		id int primary key auto_increment,</span><br><span class="line">    	name varchar(32) not null default &#x27;&#x27;</span><br><span class="line">	);</span><br><span class="line">	</span><br><span class="line">insert into t1 values(null, &#x27;zcm1&#x27;);			-- id = 1</span><br><span class="line">insert into t1 values(null, &#x27;zcm2&#x27;);			-- id = 2</span><br><span class="line">insert into t1(name) values(&#x27;zcm3&#x27;);			-- id = 3</span><br><span class="line">insert into t1(id, name) values(null, &#x27;zcm4&#x27;);	-- id = 4</span><br><span class="line">insert into t1(id, name) values(666, &#x27;zcm5&#x27;);	-- id = 666</span><br><span class="line">insert into t1(id, name) values(null, &#x27;zcm6&#x27;);	-- id = 667</span><br></pre></td></tr></table></figure></li>
</ul>
</li>
<li><p>索引：优化查询速度</p>
<ul>
<li><p>索引本身占用空间，空间换时间</p>
</li>
<li><p>只对创建了索引的列有效</p>
</li>
<li><p>索引机制</p>
<ul>
<li>无索引时，全表扫描，速度慢</li>
<li>有索引，形成索引数据结构（如排序二叉树、B树、B+树等），速度快</li>
</ul>
</li>
<li><p>索引代价</p>
<ul>
<li>占用部分空间</li>
<li>对数据的 <strong>增删改</strong> 有影响（需对索引结构进行维护）</li>
</ul>
</li>
<li><p>由于 <strong>查</strong> 是最常用的操作，所以采用索引优化查询速度</p>
</li>
<li><p>索引类型</p>
<ul>
<li><p>主键索引：主键自动的为主索引</p>
</li>
<li><p>唯一索引：<code>unique</code></p>
</li>
<li><p>普通索引：<code>index</code></p>
</li>
<li><p>全文索引：<code>fulltext</code>（适用于 <code>MyISAM</code>）</p>
<p>开发中一般不适用 <code>mysql</code> 自带的全文索引，考虑使用全文搜索 <code>Solr</code> 和 <code>ElasticSearch(ES)</code></p>
</li>
</ul>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">create table t1 (</span><br><span class="line">    	id1 int primary key,		-- 添加主键索引法1</span><br><span class="line">    	id2 int,</span><br><span class="line">		id3 int unique,				-- 添加唯一索引法1</span><br><span class="line">    	id4 int,</span><br><span class="line">    	id5 int,</span><br><span class="line">    	id6 int</span><br><span class="line">	);</span><br><span class="line">-- 添加主键索引法2</span><br><span class="line">alter table t1 add primary key (id2);	-- 已有则失败，只能有一个 primary key</span><br><span class="line">-- 添加唯一索引法2（不可重复）</span><br><span class="line">create unique index id4_index on t1(id4);</span><br><span class="line">-- 添加普通索引法1（可重复）</span><br><span class="line">create index id5_index on t1(id5);</span><br><span class="line">-- 添加普通索引法2（可重复）</span><br><span class="line">alter table t1 add index id6_index on t1(id6);</span><br><span class="line"></span><br><span class="line">-- 查询表的索引法1</span><br><span class="line">show index from t1;</span><br><span class="line">-- 查询表的索引法2</span><br><span class="line">show indexes from t1;</span><br><span class="line">-- 查询表的索引法3</span><br><span class="line">show keys from t1;</span><br><span class="line"></span><br><span class="line">-- 删除主键索引</span><br><span class="line">alter table t1 drop primary key;</span><br><span class="line">-- 删除唯一索引</span><br><span class="line">drop index id4_index on t1;</span><br><span class="line">-- 删除普通索引</span><br><span class="line">drop index id5_index on t1;</span><br><span class="line"></span><br><span class="line">-- 修改索引 = 先删除再添加</span><br></pre></td></tr></table></figure>
</li>
<li><p>创建索引规则</p>
<ul>
<li>较频繁作为查询条件的字段应该创建索引</li>
<li>唯一性太差的字段不适合建索引（即使频繁使用），如性别</li>
<li>更新频繁的字段不适合建索引</li>
<li>不会出现在 <code>where</code> 语句中的字段不该创建索引</li>
</ul>
</li>
</ul>
</li>
<li><p>事务</p>
<ul>
<li><p>用于保证数据的一致性，由一组相关的 <code>dml语句</code> （增删改）组成</p>
</li>
<li><p>该组 <code>dml语句</code> 要么全部执行成功，要么全部执行失败</p>
</li>
<li><p>事务操作往往和锁相关</p>
</li>
<li><p><code>mysql事务</code> 的几个重要操作</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">create table t1 (</span><br><span class="line">		id int,</span><br><span class="line">    	name varchar(255)</span><br><span class="line">	);</span><br><span class="line"></span><br><span class="line">-- 1.开始事务</span><br><span class="line">start transaction;</span><br><span class="line"></span><br><span class="line">-- 2.设置保存点</span><br><span class="line">savepoint a;</span><br><span class="line">-- 执行 dml 操作</span><br><span class="line">insert into t1 values(100, &#x27;zcm&#x27;);</span><br><span class="line">-- 再设置一个保存点</span><br><span class="line">savepoint b;</span><br><span class="line">-- 执行 dml 操作</span><br><span class="line">insert into t1 values(200, &#x27;try&#x27;);</span><br><span class="line"></span><br><span class="line">-- 3.回退到保存点 b</span><br><span class="line">rollback to b</span><br><span class="line">-- 回退到保存点 a，此时保存点 a 之后保存点 b 被删除：不可先到 a 再到 b</span><br><span class="line">rollback to a</span><br><span class="line">-- 回退到事务开始，保存点 a、b 都被删除</span><br><span class="line">rollback;</span><br><span class="line"></span><br><span class="line">-- 4.提交事务</span><br><span class="line">-- 提交任务后之前的回退点都被删除、释放锁、数据生效</span><br><span class="line">-- 其他会话/连接可查看修改后的新数据</span><br><span class="line">commit;</span><br></pre></td></tr></table></figure>
</li>
<li><p>细节和注意事项</p>
<ul>
<li>如果没有开启事务，<code>dml 语句</code>自动提交，不能回滚</li>
<li>如果开启事务但没有创建保存点，执行 <code>rollback;</code> 会回退到事务开始状态</li>
<li>可创建多个保存点，可用 <code>rollback to xx</code> 回退到指定保存点，此时该保存点之后的保存点会被删除</li>
<li><code>mysql</code> 的事务机制需要 <code>InnoDB</code> 引擎支持，<code>MyISAM</code> 不支持</li>
<li>开启事务可以用 <code>start transaction</code> 或 <code>set autocommit=off</code>；一般用第一个</li>
<li>事务具有 <code>ACID</code> 特性：<code>Astomicity</code> 原子性、<code>Consistency</code>一致性、<code>Isolation</code>隔离性、<code>Durability</code>持久性</li>
</ul>
</li>
</ul>
</li>
<li><p><a target="_blank" rel="noopener" href="https://zhuanlan.zhihu.com/p/117476959">隔离级别</a></p>
<ul>
<li><p>多个连接开启各自的事务操作时，数据库负责隔离操作，以保证各个连接在获取数据时的准确性</p>
</li>
<li><p>定义<strong>事务与事务之间的隔离程序</strong></p>
</li>
<li><p>不考虑隔离性会造成以下问题：</p>
<ul>
<li>脏读（<code>dirty read</code>）：一个事务读取另一个事务<strong>未提交的增删改操作</strong></li>
<li>不可重复读（<code>nonrepeatable read</code>）：同一查询在同一事务中多次进行，由于其他事务<strong>已提交的修改和删除操作</strong>，返回不同的结果</li>
<li>幻读（<code>phantom read</code>）：同一查询在同一事务中多次进行，由于其他事务<strong>已提交的插入操作</strong>，返回不同的结果</li>
</ul>
</li>
<li><p>从上往下，隔离强度逐渐增强，性能逐渐变差</p>
<p>读未提交 <code>READ UNCOMMITTED</code> ：不加锁读</p>
<p>读提交 <code>READ COMMITTED</code> ：不加锁读</p>
<p>可重复读 <code>REPEATABLE READ</code> （<code>mysql</code> 默认隔离级别，可在配置文件 <code>my.ini</code> 修改）：不加锁读</p>
<p>串行化 <code>SERIALIZABLE</code> ：加锁读</p>
 <img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/%E9%9B%B6%E5%9F%BA%E7%A1%80%E5%AD%A6Java/HGJ7M6.png" alt="HGJ7M6.png" style="zoom:67%;" />

<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">-- 1.查看当前会话隔离级别</span><br><span class="line">select @@tx_isolation</span><br><span class="line">-- 2.查看系统隔离级别</span><br><span class="line">select @@global.tx_isolation</span><br><span class="line">-- 3.设置当前会话隔离级别为 读未提交</span><br><span class="line">set session transaction isolation level read uncommitted</span><br><span class="line">-- 4.设置系统隔离级别为：串行化</span><br><span class="line">set global transaction isolation level serializable</span><br></pre></td></tr></table></figure></li>
</ul>
</li>
<li><p>存储引擎</p>
<ul>
<li><p>表类型由存储引擎（Storage Engines）决定</p>
</li>
<li><p>MySQL 数据主要支持六种类型；分别是：</p>
<ul>
<li>事务安全型：<code>InnoDB</code></li>
<li>非事务安全型：<code>CSV、Memory、ARCHIVE、MRG_MYISAM、MYISAM</code></li>
</ul>
</li>
<li><p>常见存储引擎特点</p>
<ul>
<li><p><code>MyISAM</code> 不支持事务、也不支持外键，但其访问速度快，对事务完整性没有要求</p>
</li>
<li><p><code>InnoDB</code> 存储引擎提供了具有提交、回滚和崩溃恢复能力的事务安全。但是比起 <code>MyISAM</code> 存储引擎，<code>InnoDB</code> 写的处理效率差一些并且会占用更多的磁盘空间以保留数据和索引</p>
</li>
<li><p><code>MEMORY</code> 存储引擎使用存在内存中的内容来创建表。每个 <code>MEMORY</code> 表只实际对应今一个磁盘文件。<code>MEMORY</code> 类型的表访问非常得快，因为它的数据是放在内存中的，并且默认使用 <code>HASH</code> 索引。但是一旦<code>MySQL</code> 服务关闭，表中的数据就会丢失掉，表的结构还在</p>
</li>
</ul>
<p><a target="_blank" rel="noopener" href="https://imgtu.com/i/HGxam4"><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/%E9%9B%B6%E5%9F%BA%E7%A1%80%E5%AD%A6Java/HGxam4.png" alt="HGxam4.png"></a></p>
</li>
</ul>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">-- 查看所有的存储引擎</span><br><span class="line">show engines;</span><br><span class="line"></span><br><span class="line">-- 1.InnoDB: </span><br><span class="line">-- 支持事务、支持外键、支持行级锁</span><br><span class="line">## 适用于需支持事务</span><br><span class="line"></span><br><span class="line">-- 2.MyISAM: </span><br><span class="line">-- 添加速度块、不支持外键和事务、支持表级锁</span><br><span class="line">## 适用于无需事务，处理基本的 CRUD 操作</span><br><span class="line"></span><br><span class="line">-- 3.Memory: </span><br><span class="line">-- 内存级引擎，数据存储在内存中（无 IO 读写），执行速度快</span><br><span class="line">-- 若关闭 MySQL 则数据丢失，但是表结构还在</span><br><span class="line">-- 默认支持索引（hash 表）</span><br><span class="line">## 适用于频繁修改且不重要的数据处理，如用户在线状态</span><br><span class="line"></span><br><span class="line">-- 修改存储引擎</span><br><span class="line">alter table table_name engine = engine_name;</span><br></pre></td></tr></table></figure>
</li>
<li><p>视图</p>
<ul>
<li><p>视图是一个虚拟表，其内容由查询定义。其数据来自对应的<strong>真实表（基表）</strong>，增删改查也是在修改基表数据</p>
</li>
<li><p>视图的基本使用</p>
<ul>
<li>在数据库中视图有且只有一个对应的视图结构文件，<strong>没有数据文件</strong></li>
<li><strong>可以通过视图修改基表；基表的改变也会影响视图</strong></li>
<li>视图中可以再使用视图，其数据仍然来自基表，修改时都会改变（本质数据在基表）</li>
</ul>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">-- 1.创建视图: create view view_name as select...</span><br><span class="line">create view emp_view01</span><br><span class="line">	as</span><br><span class="line">	select empno, ename, job, deptno</span><br><span class="line">	from emp;</span><br><span class="line">	</span><br><span class="line">-- 2.更新视图: alter view view_name as select...</span><br><span class="line">alter view emp_view01</span><br><span class="line">	as</span><br><span class="line">	select empno, ename, job</span><br><span class="line">	from emp;</span><br><span class="line">	</span><br><span class="line">-- 3.查看该视图所用的指令: show create view view_name</span><br><span class="line">show create view emp_view01;</span><br><span class="line"></span><br><span class="line">-- 4.删除视图: drop view view_name1, view_name2</span><br><span class="line">drop view emp_view01;</span><br><span class="line"></span><br><span class="line">-- 细节1.修改视图，会影响基表</span><br><span class="line">update emp_view01</span><br><span class="line">	set job = &#x27;MANAGER&#x27;</span><br><span class="line">	where empno = 7369;</span><br><span class="line"></span><br><span class="line">-- 细节1.修改基表，会影响视图</span><br><span class="line">update emp</span><br><span class="line">	set job = &#x27;SALESMAN&#x27;</span><br><span class="line">	where empno = 7369</span><br><span class="line">	</span><br><span class="line">-- 细节2.视图中的视图，其修改时 emp_view01、emp 表都会改变</span><br><span class="line">create view emp_view02</span><br><span class="line">	as</span><br><span class="line">	select empno, ename</span><br><span class="line">	from emp_view01;</span><br><span class="line">	</span><br><span class="line">-- 针对 emp、dept、salgrade 三张表，创建一个视图</span><br><span class="line">-- 可以显示 empno、ename、dname、grade</span><br><span class="line">create view emp_dept_salgrade</span><br><span class="line">	as</span><br><span class="line">	select empno, ename, dname, grade</span><br><span class="line">	from emp join dept</span><br><span class="line">	on emp.deptno = dept.deptno</span><br><span class="line">	join salgrade</span><br><span class="line">	on emp.sal between losal and hisal;</span><br><span class="line">-- 或</span><br><span class="line">create view emp_dept_salgrade</span><br><span class="line">	as</span><br><span class="line">	select empno, ename, dname, grade</span><br><span class="line">	from emp, dept, salgrade</span><br><span class="line">	where emp.deptno = dept.deptno and </span><br><span class="line">	emp.sal between losal and hisal;</span><br></pre></td></tr></table></figure>
</li>
<li><p>视图实践</p>
<ul>
<li><strong>安全</strong>：一些数据表有着重要的信息。有些字段是保密的，不能让用户直接看到。这时就可以创建一个视图，在这张视图中只保留一部分字段。这样，用户就可以查询自己需要的字段，不能查看保密的字段</li>
<li><strong>性能</strong>：关系数据库的数据常常会分表存储，使用外键建立这些表的之间关系。这时，数据库查询通常会用到连接（JOIN）。这样做不但麻烦，效率相对也比较低。如果建立一个视图，将相关的表和字段组合在一起，就可以避免使用JOIN查询数据</li>
<li><strong>灵活</strong>：如果系统中有一张旧的表，这张表由于设计的问题，即将被废弃。然而，很多应用都是基于这张表，不易修改。这时就可以建立一张视图，视图中的数据直接映射到新建的表。这样，就可以少做很多改动，也达到了升级数据表的目的</li>
</ul>
</li>
</ul>
</li>
<li><p>MySQL 用户管理</p>
<ul>
<li><p>不同的数据库用户登陆后，根据不同的权限，可以操作的数据对象不同</p>
</li>
<li><p>MySQL 中所有用户都存储在<code>数据库 mysql</code> 中的 <code>user 表</code>中</p>
<ul>
<li>host：允许登录的”位置”，localhost 表该用户只允许在本机登录，也可指定 IP</li>
<li>user：用户名</li>
<li><code>anthentication_string</code>：密码，是通过 <code>mysql</code> 的 password() 函数加密后的字符串</li>
</ul>
</li>
<li><p>用户管理常用操作</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">-- 1.创建用户: create user &#x27;用户名&#x27;@&#x27;允许登录的位置&#x27; [identified by &#x27;密码&#x27;]</span><br><span class="line">create user &#x27;zcm&#x27;@&#x27;localhost&#x27; identified by &#x27;123456&#x27;;</span><br><span class="line">-- 若不指定&#x27;允许登录的位置&#x27;，则为&#x27;%&#x27;&#x27;，表示所有 ip 都有连接权限</span><br><span class="line">-- 不指定密码则密码为空</span><br><span class="line">-- 等价于 create user &#x27;jack&#x27;@&#x27;%&#x27;;</span><br><span class="line">create user &#x27;jack&#x27;;</span><br><span class="line"></span><br><span class="line">-- 2.删除用户: drop user &#x27;用户名&#x27;@&#x27;允许登录的位置&#x27;</span><br><span class="line">drop user &#x27;zcm&#x27;@&#x27;localhost&#x27;;</span><br><span class="line">-- 若该用户&#x27;允许登录的位置&#x27;是&#x27;%&#x27;，则无需指定&#x27;允许登录的位置&#x27;，否则需明确指定</span><br><span class="line">-- 等价于 drop user &#x27;jack&#x27;@&#x27;%&#x27;;</span><br><span class="line">drop user &#x27;jack&#x27;;</span><br><span class="line"></span><br><span class="line">-- 3.修改自身密码: set password = password(&#x27;密码&#x27;)</span><br><span class="line">set password = password(&#x27;zcm&#x27;);</span><br><span class="line"></span><br><span class="line">-- 4.修改他人密码（需要有相应权限）: set password for &#x27;用户名&#x27;@&#x27;允许登录的位置&#x27; = password(&#x27;密码&#x27;)</span><br></pre></td></tr></table></figure>
</li>
<li><p>MySQL 中的权限<img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/%E9%9B%B6%E5%9F%BA%E7%A1%80%E5%AD%A6Java/HJijMt.png" alt="HJijMt.png"></p>
<ul>
<li>给用户授权： <code>grant 权限列表 on 库.对象名 to &#39;用户名&#39;@&#39;登陆位置&#39; [identified by &#39;密码&#39;]</code><ul>
<li>权限列表有多个用逗号间隔： <code>grant select, delect, create ... on ...</code></li>
<li>赋予所有权限：<code>grant all [privileges] on ...</code></li>
<li><code>*.*</code> 表示本系统中所有数据库的所有对象（表、视图、存储过程）</li>
<li><code>库.*</code> 表示某个数据库中的所有数据对象（表、视图、存储过程）</li>
<li><code>idetified by</code> 可省略；若不省略<ul>
<li>如该用户存在，则表示修改其密码</li>
<li>如该用户不存在，则创建用户</li>
</ul>
</li>
</ul>
</li>
<li>回收用户权限：<code>revoke 权限列表 on 库.对象名 from &#39;用户名&#39;@&#39;登陆位置&#39;</code></li>
<li>权限生效指令：如权限没有生效，则需刷新 <code>flush privileges</code>（MySQL 5.7 之后一般即时生效）</li>
</ul>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">-- 1.给用户授权 </span><br><span class="line">-- 使用户 zcm 可以查看、添加数据库 testdb 中 news 表的数据</span><br><span class="line">grant select, insert on testdb.news to &#x27;zcm&#x27;@&#x27;localhost&#x27; identified by &#x27;123456&#x27;;</span><br><span class="line">-- 创建权限堪比 root 的用户</span><br><span class="line">grant all on *.* to ...</span><br><span class="line"></span><br><span class="line">-- 2.回收用户权限</span><br><span class="line">-- 回收 zcm 对数据库 testdb 中 news 表的权限</span><br><span class="line">revoke select, insert on testdb.news from &#x27;zcm&#x27;@&#x27;localhost&#x27;;</span><br><span class="line">-- 或</span><br><span class="line">revoke all on testdb.news from &#x27;zcm&#x27;@&#x27;localhost&#x27;;</span><br><span class="line"></span><br><span class="line">-- 3.权限生效指令</span><br><span class="line">flush privileges;</span><br></pre></td></tr></table></figure>
</li>
<li><p>课后练习</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">-- 显示各月倒数第四天受雇的所有员工</span><br><span class="line">select * from emp</span><br><span class="line">	where last_day(hiredate) - 3 = hiredate;</span><br><span class="line">	</span><br><span class="line">-- 显示姓名不带 R 的员工信息</span><br><span class="line">select * from emp</span><br><span class="line">	where ename not like &#x27;%R%&#x27;;</span><br><span class="line">	</span><br><span class="line">-- 显示所有员工的姓名，用 a 替换所有 A</span><br><span class="line">select replace(ename, &#x27;A&#x27;, &#x27;a&#x27;) from emp;</span><br><span class="line"></span><br><span class="line">-- 以年月日方式显示所有员工的服务年限</span><br><span class="line">select ename, floor(datediff(now(), hiredate) / 365) as &#x27;年&#x27;,</span><br><span class="line">	floor(datediff(now(), hiredate) % 365 / 31) as &#x27;月&#x27;,</span><br><span class="line">	floor(datediff(now(), hiredate) % 31) as &#x27;天&#x27;</span><br><span class="line">	from emp;</span><br><span class="line">	</span><br><span class="line">-- 至少有一个员工的部门</span><br><span class="line">select count(*) as num, deptno</span><br><span class="line">	from emp</span><br><span class="line">	group by deptno</span><br><span class="line">	having num &gt; 1;</span><br><span class="line">	</span><br><span class="line">-- 受雇日期晚于其直接上级的员工</span><br><span class="line">select worker.ename, worker.hiredate, leader.ename, leader.hiredate</span><br><span class="line">	from emp as worker, emp as leader</span><br><span class="line">	where worker.mgr = leader.empno and</span><br><span class="line">	worker.hiredate &gt; leader.hiredate;</span><br><span class="line"></span><br><span class="line">-- 流出部门名称和其员工信息，同时列出没有员工的部门</span><br><span class="line">select dname, emp.*</span><br><span class="line">	from dept left join emp</span><br><span class="line">	on dept.deptno = emp.deptno;</span><br></pre></td></tr></table></figure></li>
</ul>
</li>
</ul>
<h3 id="JDBC和数据库连接池"><a href="#JDBC和数据库连接池" class="headerlink" title="JDBC和数据库连接池"></a>JDBC和数据库连接池</h3><ul>
<li><p>JDBC 是为访问不同的数据库提供的统一接口，为使用者屏蔽了细节问题</p>
</li>
<li><p>Java 程序员使用 JDBC 可以连接任何提供了 JDBC 驱动程序的数据库系统，从而完成对数据库的各种操作</p>
</li>
<li><p>JDBC 原理示意图</p>
<p>JDBC 是 Java 提供一套用于数据库操作的接口 API， Java 程序员只需面向这套接口编程即可，不同数据库厂商针对这套接口提供不同实现</p>
<p><a target="_blank" rel="noopener" href="https://imgtu.com/i/HYQkgf"><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/%E9%9B%B6%E5%9F%BA%E7%A1%80%E5%AD%A6Java/HYQkgf.png" alt="HYQkgf.png"></a></p>
</li>
<li><p>JDBC 程序编程步骤</p>
<ul>
<li>注册驱动：加载 <code>Driver</code> 类</li>
<li>获取链接：得到 <code>Connection</code></li>
<li>执行增删改查：发送 <code>SQL语句</code> 给 <code>mysql</code> 执行</li>
<li>释放资源：关闭相关连接</li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> SQLException &#123;</span><br><span class="line">    <span class="comment">// 前置工作: 引入 jar 包</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 1.注册驱动</span></span><br><span class="line">    <span class="type">Driver</span> <span class="variable">driver</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Driver</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 2.得到连接</span></span><br><span class="line">    <span class="comment">// (1) &#x27;jdbc:mysql://&#x27; 规定表示协议，通过 jdbc 方式连接 mysql</span></span><br><span class="line">    <span class="comment">// (2) localhost 主机，可以是 ip 地址</span></span><br><span class="line">    <span class="comment">// (3) 3306 mysql 服务监听的端口</span></span><br><span class="line">    <span class="comment">// (4) db01 连接到 mysql DBMS 对应的数据库</span></span><br><span class="line">    <span class="comment">// (5) mysql 连接的本质即 socket 连接</span></span><br><span class="line">    <span class="type">String</span> <span class="variable">url</span> <span class="operator">=</span> <span class="string">&quot;jdbc:mysql://localhost:3306/db01&quot;</span>;</span><br><span class="line">    <span class="comment">// 将用户名和密码封装到 Properties 对象</span></span><br><span class="line">    <span class="type">Properties</span> <span class="variable">properties</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Properties</span>();</span><br><span class="line">    <span class="comment">// user 和 password 字段是规定好的，根据实际填写对应值</span></span><br><span class="line">    properties.setProperty(<span class="string">&quot;user&quot;</span>, <span class="string">&quot;root&quot;</span>);</span><br><span class="line">    properties.setProperty(<span class="string">&quot;password&quot;</span>, <span class="string">&quot;zcm&quot;</span>);</span><br><span class="line">    <span class="comment">// connect 即网络连接</span></span><br><span class="line">    <span class="type">Connection</span> <span class="variable">connect</span> <span class="operator">=</span> driver.connect(url, properties);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 3.执行 sql</span></span><br><span class="line">    <span class="type">String</span> <span class="variable">sql</span> <span class="operator">=</span> <span class="string">&quot;insert into actor values(null, &#x27;刘德华&#x27;, &#x27;男&#x27;, &#x27;1970-11-11&#x27;, &#x27;110&#x27;)&quot;</span>;</span><br><span class="line">    <span class="comment">// 用于执行静态 sql 语句并返回其生成的结果对象</span></span><br><span class="line">    <span class="type">Statement</span> <span class="variable">statement</span> <span class="operator">=</span> connect.createStatement();</span><br><span class="line">    <span class="comment">// 如果是 dml（增删改） 语句，则用 executeUpdate 返回的是影响行数</span></span><br><span class="line"> 	<span class="comment">// 如果是 select 语句，则用 executeQuery 返回的是结果集</span></span><br><span class="line">    <span class="type">int</span> <span class="variable">rows</span> <span class="operator">=</span> statement.executeUpdate(sql);</span><br><span class="line">    System.out.println(rows &gt; <span class="number">0</span> ? <span class="string">&quot;成功&quot;</span> : <span class="string">&quot;失败&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 4.关闭连接，释放资源</span></span><br><span class="line">    statement.close();</span><br><span class="line">    connect.close();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>数据库连接方式</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 数据库连接方式1</span></span><br><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">connect01</span><span class="params">()</span> <span class="keyword">throws</span> SQLException &#123;</span><br><span class="line">    <span class="comment">// 1.注册驱动</span></span><br><span class="line">    <span class="type">Driver</span> <span class="variable">driver</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Driver</span>();</span><br><span class="line">    <span class="comment">// 2.得到连接</span></span><br><span class="line">    <span class="type">String</span> <span class="variable">url</span> <span class="operator">=</span> <span class="string">&quot;jdbc:mysql://localhost:3306/db01&quot;</span>;</span><br><span class="line">    <span class="comment">// 将用户名和密码封装到 Properties 对象</span></span><br><span class="line">    <span class="type">Properties</span> <span class="variable">properties</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Properties</span>();</span><br><span class="line">    <span class="comment">// user 和 password 字段是规定好的，根据实际填写对应值</span></span><br><span class="line">    properties.setProperty(<span class="string">&quot;user&quot;</span>, <span class="string">&quot;root&quot;</span>);</span><br><span class="line">    properties.setProperty(<span class="string">&quot;password&quot;</span>, <span class="string">&quot;zcm&quot;</span>);</span><br><span class="line">    <span class="comment">// connect 即网络连接</span></span><br><span class="line">    <span class="type">Connection</span> <span class="variable">connect</span> <span class="operator">=</span> driver.connect(url, properties);</span><br><span class="line">    System.out.println(connect);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 数据库连接方式2：使用反射</span></span><br><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">connect02</span><span class="params">()</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">    <span class="comment">// 1.使用反射加载 Driver 类</span></span><br><span class="line">    <span class="comment">// 动态加载，更加灵活，减少依赖性</span></span><br><span class="line">    Class&lt;?&gt; aClass = Class.forName(<span class="string">&quot;com.mysql.jdbc.Driver&quot;</span>);</span><br><span class="line">    <span class="type">Driver</span> <span class="variable">driver</span> <span class="operator">=</span> (Driver)aClass.getDeclaredConstructor().newInstance();</span><br><span class="line">    <span class="comment">// 2.得到连接</span></span><br><span class="line">    <span class="type">String</span> <span class="variable">url</span> <span class="operator">=</span> <span class="string">&quot;jdbc:mysql://localhost:3306/db01&quot;</span>;</span><br><span class="line">    <span class="comment">// 将用户名和密码封装到 Properties 对象</span></span><br><span class="line">    <span class="type">Properties</span> <span class="variable">properties</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Properties</span>();</span><br><span class="line">    <span class="comment">// user 和 password 字段是规定好的，根据实际填写对应值</span></span><br><span class="line">    properties.setProperty(<span class="string">&quot;user&quot;</span>, <span class="string">&quot;root&quot;</span>);</span><br><span class="line">    properties.setProperty(<span class="string">&quot;password&quot;</span>, <span class="string">&quot;zcm&quot;</span>);</span><br><span class="line">    <span class="comment">// connect 即网络连接</span></span><br><span class="line">    <span class="type">Connection</span> <span class="variable">connect</span> <span class="operator">=</span> driver.connect(url, properties);</span><br><span class="line">    System.out.println(connect);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 数据库连接方式3：使用 DriverManager 代替 driver 进行统一管理</span></span><br><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">connect03</span><span class="params">()</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">    <span class="comment">// 1.使用反射加载 Driver 类</span></span><br><span class="line">    Class&lt;?&gt; aClass = Class.forName(<span class="string">&quot;com.mysql.jdbc.Driver&quot;</span>);</span><br><span class="line">    <span class="type">Driver</span> <span class="variable">driver</span> <span class="operator">=</span> (Driver)aClass.getDeclaredConstructor().newInstance();</span><br><span class="line">    <span class="comment">// 2.得到连接</span></span><br><span class="line">    <span class="type">String</span> <span class="variable">url</span> <span class="operator">=</span> <span class="string">&quot;jdbc:mysql://localhost:3306/db01&quot;</span>;</span><br><span class="line">    <span class="type">String</span> <span class="variable">user</span> <span class="operator">=</span> <span class="string">&quot;root&quot;</span>;</span><br><span class="line">    <span class="type">String</span> <span class="variable">password</span> <span class="operator">=</span> <span class="string">&quot;zcm&quot;</span>;</span><br><span class="line">    <span class="comment">// 注册 driver 驱动</span></span><br><span class="line">    DriverManager.registerDriver(driver);</span><br><span class="line">    <span class="comment">// connect 即网络连接</span></span><br><span class="line">    <span class="type">Connection</span> <span class="variable">connection</span> <span class="operator">=</span> DriverManager.getConnection(url, user, password);</span><br><span class="line">    System.out.println(connection);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 数据库连接方式4：使用 Class.forName 自动完成注册驱动，简化代码</span></span><br><span class="line"><span class="comment">// 推荐使用</span></span><br><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">connect04</span><span class="params">()</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">    <span class="comment">// 1.使用反射加载 Driver 类</span></span><br><span class="line">    <span class="comment">// 在加载 Driver 类时自动完成注册驱动，简化代码</span></span><br><span class="line">    Class.forName(<span class="string">&quot;com.mysql.jdbc.Driver&quot;</span>);</span><br><span class="line">    <span class="comment">// 2.得到连接</span></span><br><span class="line">    <span class="type">String</span> <span class="variable">url</span> <span class="operator">=</span> <span class="string">&quot;jdbc:mysql://localhost:3306/db01&quot;</span>;</span><br><span class="line">    <span class="type">String</span> <span class="variable">user</span> <span class="operator">=</span> <span class="string">&quot;root&quot;</span>;</span><br><span class="line">    <span class="type">String</span> <span class="variable">password</span> <span class="operator">=</span> <span class="string">&quot;zcm&quot;</span>;</span><br><span class="line">    <span class="comment">// connect 即网络连接</span></span><br><span class="line">    <span class="type">Connection</span> <span class="variable">connection</span> <span class="operator">=</span> DriverManager.getConnection(url, user, password);</span><br><span class="line">    System.out.println(connection);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 数据库连接方式5：在方式 4 的基础上改进，增加配置文件，让连接 mysql 更加灵活</span></span><br><span class="line"><span class="comment">// 推荐使用，最常使用</span></span><br><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">connect05</span><span class="params">()</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">    <span class="type">Properties</span> <span class="variable">properties</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Properties</span>();</span><br><span class="line">    properties.load(<span class="keyword">new</span> <span class="title class_">FileInputStream</span>(<span class="string">&quot;src\\mysql.properties&quot;</span>));</span><br><span class="line">    <span class="type">String</span> <span class="variable">url</span> <span class="operator">=</span> properties.getProperty(<span class="string">&quot;url&quot;</span>);</span><br><span class="line">    <span class="type">String</span> <span class="variable">user</span> <span class="operator">=</span> properties.getProperty(<span class="string">&quot;user&quot;</span>);</span><br><span class="line">    <span class="type">String</span> <span class="variable">password</span> <span class="operator">=</span> properties.getProperty(<span class="string">&quot;password&quot;</span>);</span><br><span class="line">    <span class="type">String</span> <span class="variable">driver</span> <span class="operator">=</span> properties.getProperty(<span class="string">&quot;driver&quot;</span>);</span><br><span class="line"></span><br><span class="line">    Class.forName(driver);</span><br><span class="line"></span><br><span class="line">    <span class="type">Connection</span> <span class="variable">connection</span> <span class="operator">=</span> DriverManager.getConnection(url, user, password);</span><br><span class="line">    System.out.println(connection);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p><code>ResultSet</code> 结果集</p>
<ul>
<li>表示数据库结果集的数据表，通常执行 select 语句生成</li>
<li><code>ResultSet</code> 对象保持一个光标指向当前数据行（初始位于<strong>第一行（表头，非数据）</strong>，类似于迭代器，用 next 方法移动到下一行，常用 while 遍历）</li>
<li>如果是 <code>select</code> 语句，则用 <code>executeQuery</code> 返回的是结果集</li>
<li><code>resultSet.get数据类型(顺序下标从 1 开始 或 &quot;列名&quot;)</code></li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="type">Statement</span> <span class="variable">statement</span> <span class="operator">=</span> connection.createStatement();</span><br><span class="line"><span class="type">String</span> <span class="variable">sql</span> <span class="operator">=</span> <span class="string">&quot;select id, name, sex, borndate from actor&quot;</span>;</span><br><span class="line"><span class="type">ResultSet</span> <span class="variable">resultSet</span> <span class="operator">=</span> statement.executeQuery(sql);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 5.使用while取出数据</span></span><br><span class="line"><span class="comment">// 让光标向后移动，如果没有更多行，则返回false</span></span><br><span class="line"><span class="keyword">while</span> (resultSet.next()) &#123; </span><br><span class="line">    <span class="comment">// int id  = resultSet.getInt(1); 			// 获取该行的第1列</span></span><br><span class="line">    <span class="type">int</span> <span class="variable">id</span> <span class="operator">=</span> resultSet.getInt(<span class="string">&quot;id&quot;</span>); 			<span class="comment">// 通过列名来获取值, 推荐</span></span><br><span class="line">    <span class="type">String</span> <span class="variable">name</span> <span class="operator">=</span> resultSet.getString(<span class="string">&quot;name&quot;</span>);	</span><br><span class="line">    <span class="type">String</span> <span class="variable">sex</span> <span class="operator">=</span> resultSet.getString(<span class="string">&quot;sex&quot;</span>);</span><br><span class="line">    <span class="type">Date</span> <span class="variable">date</span> <span class="operator">=</span> resultSet.getDate(<span class="string">&quot;borndate&quot;</span>);</span><br><span class="line"></span><br><span class="line">    System.out.println(id + <span class="string">&quot;\t&quot;</span> + name + <span class="string">&quot;\t&quot;</span> + sex + <span class="string">&quot;\t&quot;</span> + date);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>Statement</p>
<ul>
<li><p>Statement 对象，用于执行静态 SQL 语句并返回其生成的结果对象</p>
</li>
<li><p>如果是 <code>dml</code>（增删改） 语句，则用 <code>executeUpdate</code> 返回的是影响行数<br>如果是 <code>select</code> 语句，则用 <code>executeQuery</code> 返回的是结果集</p>
</li>
<li><p>分类</p>
<ul>
<li><code>Statement</code>：存在 SQL 注入风险，实际开发中不使用</li>
<li><code>PreparedStatement</code>：预处理，实际开发中使用</li>
<li><code>CallableStatement</code>：存储过程</li>
</ul>
</li>
<li><p>SQL 注入是利用某些系统没有对用户输入的数据进行充分检查，而在用户输入数据中注入非法的 SQL 语句段或命令，恶意攻击数据库</p>
<p>用 <code>PrepareStatement</code> 代替 <code>Statement</code> 即可预防 SQL 注入</p>
</li>
<li><p>演示 SQL 注入</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">mysql&gt; select * from admin;</span><br><span class="line">+------+----------+</span><br><span class="line">| user | password |</span><br><span class="line">+------+----------+</span><br><span class="line">| zcm  | <span class="number">123456</span>   |</span><br><span class="line">| <span class="keyword">try</span>  | <span class="number">0317</span>     |</span><br><span class="line">+------+----------+</span><br><span class="line"></span><br><span class="line"><span class="comment">// SQL 注入    </span></span><br><span class="line"><span class="comment">// 请输入用户名称: 1&#x27; or</span></span><br><span class="line"><span class="comment">// 请输入用户密码: or &#x27;1&#x27; = &#x27;1 (又称万能密码 或 or &#x27;&#x27; = &#x27;) </span></span><br><span class="line"><span class="comment">// 此时 select 语句为</span></span><br><span class="line"><span class="comment">// select user, password from admin where user = &#x27;1&#x27; or&#x27; and password = &#x27;or &#x27;1&#x27; = &#x27;1&#x27;</span></span><br><span class="line"><span class="comment">// 上述 select 语句中 where 条件恒成立</span></span><br><span class="line">    </span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">    <span class="type">Properties</span> <span class="variable">properties</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Properties</span>();</span><br><span class="line">    properties.load(<span class="keyword">new</span> <span class="title class_">FileInputStream</span>(<span class="string">&quot;src\\mysql.properties&quot;</span>));</span><br><span class="line">    <span class="type">String</span> <span class="variable">url</span> <span class="operator">=</span> properties.getProperty(<span class="string">&quot;url&quot;</span>);</span><br><span class="line">    <span class="type">String</span> <span class="variable">user</span> <span class="operator">=</span> properties.getProperty(<span class="string">&quot;user&quot;</span>);</span><br><span class="line">    <span class="type">String</span> <span class="variable">password</span> <span class="operator">=</span> properties.getProperty(<span class="string">&quot;password&quot;</span>);</span><br><span class="line">    <span class="type">String</span> <span class="variable">driver</span> <span class="operator">=</span> properties.getProperty(<span class="string">&quot;driver&quot;</span>);</span><br><span class="line">    Class.forName(driver);</span><br><span class="line">    <span class="type">Connection</span> <span class="variable">connection</span> <span class="operator">=</span> DriverManager.getConnection(url, user, password);</span><br><span class="line">    System.out.println(connection);</span><br><span class="line"></span><br><span class="line">    <span class="type">String</span> <span class="variable">user_name</span> <span class="operator">=</span> <span class="string">&quot;&quot;</span>;</span><br><span class="line">    <span class="type">String</span> <span class="variable">user_password</span> <span class="operator">=</span> <span class="string">&quot;&quot;</span>;</span><br><span class="line">    <span class="type">Scanner</span> <span class="variable">scanner</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Scanner</span>(System.in);</span><br><span class="line">    <span class="comment">// scanner.next 遇到空格则间断</span></span><br><span class="line">    <span class="comment">// 为看到 SQL 注入效果，改为 nextLine</span></span><br><span class="line">    System.out.print(<span class="string">&quot;请输入用户名称: &quot;</span>);</span><br><span class="line">    user_name = scanner.nextLine();</span><br><span class="line">    System.out.print(<span class="string">&quot;请输入用户密码: &quot;</span>);</span><br><span class="line">    user_password = scanner.nextLine();</span><br><span class="line"></span><br><span class="line">    <span class="type">Statement</span> <span class="variable">statement</span> <span class="operator">=</span> connection.createStatement();</span><br><span class="line">    <span class="type">String</span> <span class="variable">select</span> <span class="operator">=</span> <span class="string">&quot;select user, password from admin &quot;</span> +</span><br><span class="line">        <span class="string">&quot;where user = &#x27;&quot;</span> + user_name + <span class="string">&quot;&#x27; and password = &#x27;&quot;</span> + user_password + <span class="string">&quot;&#x27;&quot;</span>;</span><br><span class="line">    System.out.println(select);</span><br><span class="line">    <span class="type">ResultSet</span> <span class="variable">resultSet</span> <span class="operator">=</span> statement.executeQuery(select);</span><br><span class="line">    <span class="keyword">if</span>(resultSet.next()) &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;登陆成功&quot;</span>);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;登陆失败&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 关闭连接，释放资源</span></span><br><span class="line">    resultSet.close();</span><br><span class="line">    statement.close();</span><br><span class="line">    connection.close();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li>
</ul>
</li>
<li><p><code>PreparedStatement</code></p>
<ul>
<li><p>用 <code>?</code> 表示参数，调用时用 <code>setXXX</code> 设置参数（见下案例）</p>
<p><code>set+对应类型(参数位置从1开始, 参数值)</code></p>
</li>
<li><p>如果是 <code>dml</code>（增删改） 语句，则用 <code>executeUpdate</code> 返回的是影响行数<br>如果是 <code>select</code> 语句，则用 <code>executeQuery</code> 返回的是结果集</p>
</li>
<li><p>优点：</p>
<ul>
<li>不再使用 + 拼接 sql 语句，减少语法错误</li>
<li>有效解决 sql 注入问题</li>
<li>大大减少编译次数，提高效率</li>
</ul>
</li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 对以上案例的修改</span></span><br><span class="line"></span><br><span class="line"><span class="type">Properties</span> <span class="variable">properties</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Properties</span>();</span><br><span class="line">properties.load(<span class="keyword">new</span> <span class="title class_">FileInputStream</span>(<span class="string">&quot;src\\mysql.properties&quot;</span>));</span><br><span class="line"><span class="type">String</span> <span class="variable">url</span> <span class="operator">=</span> properties.getProperty(<span class="string">&quot;url&quot;</span>);</span><br><span class="line"><span class="type">String</span> <span class="variable">user</span> <span class="operator">=</span> properties.getProperty(<span class="string">&quot;user&quot;</span>);</span><br><span class="line"><span class="type">String</span> <span class="variable">password</span> <span class="operator">=</span> properties.getProperty(<span class="string">&quot;password&quot;</span>);</span><br><span class="line"><span class="type">String</span> <span class="variable">driver</span> <span class="operator">=</span> properties.getProperty(<span class="string">&quot;driver&quot;</span>);</span><br><span class="line">Class.forName(driver);</span><br><span class="line"><span class="type">Connection</span> <span class="variable">connection</span> <span class="operator">=</span> DriverManager.getConnection(url, user, password);</span><br><span class="line">System.out.println(connection);</span><br><span class="line"></span><br><span class="line"><span class="type">String</span> <span class="variable">user_name</span> <span class="operator">=</span> <span class="string">&quot;&quot;</span>;</span><br><span class="line"><span class="type">String</span> <span class="variable">user_password</span> <span class="operator">=</span> <span class="string">&quot;&quot;</span>;</span><br><span class="line"><span class="type">Scanner</span> <span class="variable">scanner</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Scanner</span>(System.in);</span><br><span class="line"><span class="comment">// scanner.next 遇到空格则间断</span></span><br><span class="line"><span class="comment">// 为看到 SQL 注入效果，改为 nextLine</span></span><br><span class="line">System.out.print(<span class="string">&quot;请输入用户名称: &quot;</span>);</span><br><span class="line">user_name = scanner.nextLine();</span><br><span class="line">System.out.print(<span class="string">&quot;请输入用户密码: &quot;</span>);</span><br><span class="line">user_password = scanner.nextLine();</span><br><span class="line"></span><br><span class="line"><span class="comment">// ? 相当于占位符</span></span><br><span class="line"><span class="type">String</span> <span class="variable">select</span> <span class="operator">=</span> <span class="string">&quot;select user, password from admin &quot;</span> +</span><br><span class="line">    <span class="string">&quot;where user = ? and password = ? &quot;</span>;</span><br><span class="line"><span class="type">PreparedStatement</span> <span class="variable">preparedStatement</span> <span class="operator">=</span> connection.prepareStatement(select);</span><br><span class="line"><span class="comment">// 给 ？ 赋值</span></span><br><span class="line"><span class="comment">// set+对应类型(参数位置从1开始, 参数值)</span></span><br><span class="line">preparedStatement.setString(<span class="number">1</span>, user_name);</span><br><span class="line">preparedStatement.setString(<span class="number">2</span>, user_password);</span><br><span class="line"><span class="comment">// executeQuery 不用再给 sql 语句，否则 ？ 没有被替代</span></span><br><span class="line"><span class="type">ResultSet</span> <span class="variable">resultSet</span> <span class="operator">=</span> preparedStatement.executeQuery();</span><br><span class="line"><span class="keyword">if</span>(resultSet.next()) &#123;</span><br><span class="line">    System.out.println(<span class="string">&quot;登陆成功&quot;</span>);</span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    System.out.println(<span class="string">&quot;登陆失败&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 关闭连接，释放资源</span></span><br><span class="line">resultSet.close();</span><br><span class="line">preparedStatement.close();</span><br><span class="line">connection.close();</span><br></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// PreparedStatement 的 dml 语句演示</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 1.添加记录</span></span><br><span class="line"><span class="type">String</span> <span class="variable">create</span> <span class="operator">=</span> <span class="string">&quot;insert into admin values(?, ?)&quot;</span>;</span><br><span class="line"><span class="type">PreparedStatement</span> <span class="variable">preparedStatement</span> <span class="operator">=</span> connection.prepareStatement(create);</span><br><span class="line">preparedStatement.setString(<span class="number">1</span>, <span class="string">&quot;hyh&quot;</span>);</span><br><span class="line">preparedStatement.setString(<span class="number">2</span>, <span class="string">&quot;0102&quot;</span>);</span><br><span class="line"><span class="type">int</span> <span class="variable">rows</span> <span class="operator">=</span> preparedStatement.executeUpdate();</span><br><span class="line">System.out.println(rows &gt; <span class="number">0</span> ? <span class="string">&quot;create成功&quot;</span> : <span class="string">&quot;create失败&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 2.删除记录</span></span><br><span class="line"><span class="type">String</span> <span class="variable">delete</span> <span class="operator">=</span> <span class="string">&quot;delete from admin where user = ?&quot;</span>;</span><br><span class="line"><span class="type">PreparedStatement</span> <span class="variable">preparedStatement</span> <span class="operator">=</span> connection.prepareStatement(delete);</span><br><span class="line">preparedStatement.setString(<span class="number">1</span>, <span class="string">&quot;hyh&quot;</span>);</span><br><span class="line"><span class="type">int</span> <span class="variable">rows</span> <span class="operator">=</span> preparedStatement.executeUpdate();</span><br><span class="line">System.out.println(rows &gt; <span class="number">0</span> ? <span class="string">&quot;delete成功&quot;</span> : <span class="string">&quot;delete失败&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 3.修改记录</span></span><br><span class="line"><span class="type">String</span> <span class="variable">update</span> <span class="operator">=</span> <span class="string">&quot;update admin set password = ? where user = ?&quot;</span>;</span><br><span class="line"><span class="type">PreparedStatement</span> <span class="variable">preparedStatement</span> <span class="operator">=</span> connection.prepareStatement(update);</span><br><span class="line">preparedStatement.setString(<span class="number">1</span>, <span class="string">&quot;0111&quot;</span>);</span><br><span class="line">preparedStatement.setString(<span class="number">2</span>, <span class="string">&quot;hyh&quot;</span>);</span><br><span class="line"><span class="type">int</span> <span class="variable">rows</span> <span class="operator">=</span> preparedStatement.executeUpdate();</span><br><span class="line">System.out.println(rows &gt; <span class="number">0</span> ? <span class="string">&quot;update成功&quot;</span> : <span class="string">&quot;update失败&quot;</span>);</span><br></pre></td></tr></table></figure>
</li>
<li><p>JDBC API 小结</p>
<p><a target="_blank" rel="noopener" href="https://imgtu.com/i/HtCJeK"><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/%E9%9B%B6%E5%9F%BA%E7%A1%80%E5%AD%A6Java/HtCJeK.png" alt="HtCJeK.png"></a><br><a target="_blank" rel="noopener" href="https://imgtu.com/i/HtC8L6"><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/%E9%9B%B6%E5%9F%BA%E7%A1%80%E5%AD%A6Java/HtC8L6.png" alt="HtC8L6.png"></a></p>
</li>
<li><p><code>JDBC Utils</code></p>
<ul>
<li>操作数据库三部曲<ul>
<li>连接数据库</li>
<li>执行 SQL 语句</li>
<li>关闭资源</li>
</ul>
</li>
<li>连接数据库和关闭资源代码基本相同，用工具类完成</li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">JDBCUtils</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> String user;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> String password;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> String url;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> String driver;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 初始化上述静态变量</span></span><br><span class="line">    <span class="keyword">static</span> &#123;</span><br><span class="line">        <span class="type">Properties</span> <span class="variable">properties</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Properties</span>();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            properties.load(<span class="keyword">new</span> <span class="title class_">FileInputStream</span>(<span class="string">&quot;src\\mysql.properties&quot;</span>));</span><br><span class="line">            user = properties.getProperty(<span class="string">&quot;user&quot;</span>);</span><br><span class="line">            password = properties.getProperty(<span class="string">&quot;password&quot;</span>);</span><br><span class="line">            url = properties.getProperty(<span class="string">&quot;url&quot;</span>);</span><br><span class="line">            driver = properties.getProperty(<span class="string">&quot;driver&quot;</span>);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">            <span class="comment">// 在实际开发中一般如下处理，优点如下</span></span><br><span class="line">            <span class="comment">// 1.将编译异常转为运行异常</span></span><br><span class="line">            <span class="comment">// 2.调用者可以选择捕获该异常或继续向上抛，较为方便</span></span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 连接数据库，返回 Connection 对象</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> Connection <span class="title function_">getConnection</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> DriverManager.getConnection(url, user, password);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (SQLException e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 关闭相关资源</span></span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">        1.ResultSet 结果集</span></span><br><span class="line"><span class="comment">        2.Statement 或 PreparedStatement</span></span><br><span class="line"><span class="comment">        3.Connection</span></span><br><span class="line"><span class="comment">        PreparedStatement 是 Statement 的子类</span></span><br><span class="line"><span class="comment">        如需关闭相关资源，传入对应对象即可，否则传入 null</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">close</span><span class="params">(ResultSet set, Statement statement, Connection connection)</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">if</span>(set != <span class="literal">null</span>) &#123;</span><br><span class="line">                set.close();</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span>(statement != <span class="literal">null</span>) &#123;</span><br><span class="line">                statement.close();</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span>(connection != <span class="literal">null</span>) &#123;</span><br><span class="line">                connection.close();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">catch</span> (SQLException e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 测试 JDBCUtils 工具类</span></span><br><span class="line"><span class="comment">// insert、update、delete</span></span><br><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testDML</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="type">Connection</span> <span class="variable">connection</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="comment">// 2.sql</span></span><br><span class="line">    <span class="type">String</span> <span class="variable">sql</span> <span class="operator">=</span> <span class="string">&quot;update actor set name = ? where id = ?&quot;</span>;</span><br><span class="line">    <span class="type">PreparedStatement</span> <span class="variable">preparedStatement</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="comment">// 1.得到连接</span></span><br><span class="line">        connection = JDBCUtils.getConnection();</span><br><span class="line">        preparedStatement = connection.prepareStatement(sql);</span><br><span class="line">        preparedStatement.setString(<span class="number">1</span>, <span class="string">&quot;周杰伦&quot;</span>);</span><br><span class="line">        preparedStatement.setInt(<span class="number">2</span>, <span class="number">2</span>);</span><br><span class="line">        preparedStatement.executeUpdate();</span><br><span class="line">    &#125; <span class="keyword">catch</span> (SQLException e) &#123;</span><br><span class="line">        e.printStackTrace();</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        <span class="comment">// 3.关闭资源</span></span><br><span class="line">        JDBCUtils.close(<span class="literal">null</span>, preparedStatement, connection);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// select</span></span><br><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testSelect</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="type">Connection</span> <span class="variable">connection</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="comment">// 2.sql</span></span><br><span class="line">    <span class="type">String</span> <span class="variable">sql</span> <span class="operator">=</span> <span class="string">&quot;select * from actor&quot;</span>;</span><br><span class="line">    <span class="type">PreparedStatement</span> <span class="variable">preparedStatement</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="type">ResultSet</span> <span class="variable">resultSet</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="comment">// 1.得到连接</span></span><br><span class="line">        connection = JDBCUtils.getConnection();</span><br><span class="line">        preparedStatement = connection.prepareStatement(sql);</span><br><span class="line">        resultSet = preparedStatement.executeQuery();</span><br><span class="line">        <span class="keyword">while</span>(resultSet.next()) &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">id</span> <span class="operator">=</span> resultSet.getInt(<span class="string">&quot;id&quot;</span>);</span><br><span class="line">            <span class="type">String</span> <span class="variable">name</span> <span class="operator">=</span> resultSet.getString(<span class="string">&quot;name&quot;</span>);</span><br><span class="line">            <span class="type">String</span> <span class="variable">sex</span> <span class="operator">=</span> resultSet.getString(<span class="string">&quot;sex&quot;</span>);</span><br><span class="line">            <span class="type">Date</span> <span class="variable">borndate</span> <span class="operator">=</span> resultSet.getDate(<span class="string">&quot;borndate&quot;</span>);</span><br><span class="line">            <span class="type">String</span> <span class="variable">phone</span> <span class="operator">=</span> resultSet.getString(<span class="string">&quot;phone&quot;</span>);</span><br><span class="line">            System.out.println(id + <span class="string">&quot;\t&quot;</span> + name + <span class="string">&quot;\t&quot;</span> + sex + <span class="string">&quot;\t&quot;</span> + borndate + <span class="string">&quot;\t&quot;</span> + phone);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">catch</span> (SQLException e) &#123;</span><br><span class="line">        e.printStackTrace();</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        <span class="comment">// 3.关闭资源</span></span><br><span class="line">        JDBCUtils.close(resultSet, preparedStatement, connection);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>事务处理</p>
<ul>
<li><code>JDBC</code> 程序中当一个 <code>Connection</code> 对象创建时，默认情况下是自动提交事务：每次执行一个 <code>SQL</code> 语句时，如果执行成功，就会向数据库自动提交，而不能回滚</li>
<li><code>JDBC</code> 程序中为了让多个 <code>SQL</code> 语句作为一个整体执行，需要使用事务</li>
<li>调用 <code>Connection</code> 的 <code>setAutoCommit(false)</code>可以取消自动提交事务</li>
<li>在所有的 SQL 语句都成功执行后，调用 <code>Connection</code> 的 <code>commit()</code> 方法提交事务</li>
<li>在其中某个操作失败或出现异常时，调用 <code>Connection</code> 的 <code>rollback()</code> 方法回滚事务</li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 事务来解决</span></span><br><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">useTransaction</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="comment">// 操作转账的业务</span></span><br><span class="line">    <span class="comment">// 1.得到连接</span></span><br><span class="line">    <span class="type">Connection</span> <span class="variable">connection</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="comment">// 2.组织一个sql</span></span><br><span class="line">    <span class="type">String</span> <span class="variable">sql</span> <span class="operator">=</span> <span class="string">&quot;update account set balance = balance - 100 where id = 1&quot;</span>;</span><br><span class="line">    <span class="type">String</span> <span class="variable">sql2</span> <span class="operator">=</span> <span class="string">&quot;update account set balance = balance + 100 where id = 2&quot;</span>;</span><br><span class="line">    <span class="type">PreparedStatement</span> <span class="variable">preparedStatement</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="comment">// 3.创建 PreparedStatement 对象</span></span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="comment">// 在默认情况下，connection 是默认自动提交</span></span><br><span class="line">        connection = JDBCUtils.getConnection(); </span><br><span class="line">        <span class="comment">// 将 connection 设置为不自动提交，即开启事务</span></span><br><span class="line">        connection.setAutoCommit(<span class="literal">false</span>);</span><br><span class="line">        preparedStatement = connection.prepareStatement(sql);</span><br><span class="line">        preparedStatement.executeUpdate(); <span class="comment">// 执行第 1 条 sql</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 制造异常，测试回滚</span></span><br><span class="line">        <span class="comment">// int i = 1 / 0; </span></span><br><span class="line">        preparedStatement = connection.prepareStatement(sql2);</span><br><span class="line">        preparedStatement.executeUpdate(); <span class="comment">// 执行第 2 条 sql</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 这里提交事务</span></span><br><span class="line">        connection.commit();</span><br><span class="line">    &#125; <span class="keyword">catch</span> (SQLException e) &#123;</span><br><span class="line">        <span class="comment">// 这里我们可以进行回滚，即撤销执行的 SQL</span></span><br><span class="line">        <span class="comment">// 默认回滚到事务开始的状态.</span></span><br><span class="line">        System.out.println(<span class="string">&quot;执行发生了异常，撤销执行的 sql&quot;</span>);</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            connection.rollback();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (SQLException throwables) &#123;</span><br><span class="line">            throwables.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">        e.printStackTrace();</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        <span class="comment">// 关闭资源</span></span><br><span class="line">        JDBCUtils.close(<span class="literal">null</span>, preparedStatement, connection);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>批处理<strong>？？？不行，不知为何</strong></p>
<ul>
<li><p>当需要成批插入或者更新记录时。可以采用Java的批量更新机制，这一机制允许多条语句一次性提交给数据库批量处理。通常情况下比单独提交处理更有效率</p>
</li>
<li><p>JDBC 的批量处理语句包括下面方法：</p>
<p><code>addBatch()</code>：添加需要批量处理的SQL语句或参数</p>
<p><code>executeBatch()</code>：执行批量处理语句</p>
<p><code>clearBatch()</code>：清空批处理包的语句</p>
</li>
<li><p><strong>UDBC 连接 MySQL 时，如果要使用批处理功能</strong></p>
<p><strong>需在 <code>url</code> 中加参数  <code>?rewriteBatchedStatements=true</code></strong></p>
</li>
<li><p>批处理往往和 <code>PreparedStatement</code> 一起搭配使用，可以既减少编译次数，又减少运行次数，效率大大提高</p>
</li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testDML</span><span class="params">()</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">    <span class="type">Connection</span> <span class="variable">connection</span> <span class="operator">=</span> JDBCUtils.getConnection();</span><br><span class="line">    <span class="type">String</span> <span class="variable">sql</span> <span class="operator">=</span> <span class="string">&quot;insert into admin values(?, ?)&quot;</span>;</span><br><span class="line">    <span class="type">PreparedStatement</span> <span class="variable">preparedStatement</span> <span class="operator">=</span> connection.prepareStatement(sql);</span><br><span class="line">    System.out.println(<span class="string">&quot;开始执行&quot;</span>);</span><br><span class="line">    <span class="type">long</span> <span class="variable">start</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">5000</span>; i++) &#123;</span><br><span class="line">        preparedStatement.setString(<span class="number">1</span>, <span class="string">&quot;周杰伦&quot;</span> + i);</span><br><span class="line">        preparedStatement.setString(<span class="number">2</span>, <span class="string">&quot;666&quot;</span>);</span><br><span class="line">        <span class="comment">// 不马上执行 sql 语句，将其加入到批处理包中</span></span><br><span class="line">        <span class="comment">// preparedStatement.executeUpdate();</span></span><br><span class="line">        <span class="comment">// 1.addBatch()</span></span><br><span class="line">        preparedStatement.addBatch();</span><br><span class="line">        <span class="comment">// 当有 1000 条记录时再批量执行</span></span><br><span class="line">        <span class="keyword">if</span>((i + <span class="number">1</span>) % <span class="number">1000</span> == <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="comment">// 2.executeBatch()</span></span><br><span class="line">            preparedStatement.executeBatch();</span><br><span class="line">            <span class="comment">// 3.clearBatch() 清空</span></span><br><span class="line">            preparedStatement.clearBatch();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">long</span> <span class="variable">end</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line">    System.out.println(<span class="string">&quot;执行结束，耗时 =&quot;</span> + (end - start));</span><br><span class="line">    JDBCUtils.close(<span class="literal">null</span>, preparedStatement, connection);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li><p><a target="_blank" rel="noopener" href="https://www.bilibili.com/video/BV1fh411y7R8?p=840">批处理源码分析</a></p>
<p>batch 里面有个 <code>ArrayList</code>，满后按照 1.5 倍扩容，添加到指定数量时进行批量处理</p>
</li>
</ul>
</li>
<li><p>数据库连接池 <code>connection pool</code>（类似于线程池）</p>
<ul>
<li><p>数据库连接池技术用于解决传统开发中的数据库连接问题</p>
</li>
<li><p>传统连接的问题</p>
<ul>
<li>传统的 <code>JDBC</code> 数据库连接使用 <code>DriverManager</code> 来获取，每次向数据库建立连接的时候都要将Connection加载到内存中，再验证IP地址，用户名和密码（0.05s ～1s时间），需要数据库连接的时候，就向数据库要求一个，频繁的进行数据库连接操作将占用很多的系统资源，容易造成服务器崩溃</li>
<li>每一次数据库连接，使用完后都得断开，如果程序出现异常而未能关闭，将导致数据库内存泄漏，最终将导致重启数据库</li>
<li>传统获取连接的方式，不能控制创建的连接数量，如连接过多，也可能导致内存泄漏， MySQL 崩溃</li>
</ul>
</li>
<li><p>数据库连接池机制</p>
<ul>
<li>预先在缓冲池中放入一定数量的连接，当需要建立数据库连接时，只需从“缓冲池”中取出一个，使用完毕之后再放回去</li>
<li>数据库连接池负责分配、管理和释放数据库连接，它允许应用程序重复使用一个现有的数据库连接，而不是重新建立一个（<strong>连接可复用</strong>）</li>
<li>当应用程序向连接池请求的连接数超过 <code>最大连接数量</code> 时，这些请求将被加入到等待队列中</li>
</ul>
</li>
<li><p>数据库连接池种类（目前最常用<code> Durid、C3P0</code>）</p>
<ul>
<li><code>JDBC</code> 的数据库连接池使用 <code>javax.sql.DataSource</code> 来表示， <code>DataSource</code> 只是一个接口，该接口通常由第三方提供实现</li>
<li><strong><code>C3P0</code></strong> 数据库连接池，速度相对较慢，稳定性不错（hibernate， spring）</li>
<li><code>DBCP</code> 数据库连接池，速度相对 <code>C3P0</code> 较快，但不稳定</li>
<li><code>Proxool</code> 数据库连接池，有监控连接池状态的功能，稳定性较c3p0差一点. </li>
<li><code>BoneCP</code> 数据库连接池，速度快</li>
<li><strong><code>Druid(德鲁伊)</code></strong> 是阿里提供的数据库连接池，集 <code>DBCP、C3P0、Proxool</code> 优点于一身的数据库连接池</li>
</ul>
</li>
<li><p>C3P0 使用演示（老牌，较慢但是稳定）</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 方式1，手动配置</span></span><br><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testC3P0_01</span><span class="params">()</span> <span class="keyword">throws</span> Exception&#123;</span><br><span class="line">    <span class="comment">// 1.创建数据源对象</span></span><br><span class="line">    <span class="type">ComboPooledDataSource</span> <span class="variable">comboPooledDataSource</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ComboPooledDataSource</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 2.通过配置文件 mysql.properties 获取相关信息</span></span><br><span class="line">    <span class="type">Properties</span> <span class="variable">properties</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Properties</span>();</span><br><span class="line">    properties.load(<span class="keyword">new</span> <span class="title class_">FileInputStream</span>(<span class="string">&quot;src\\mysql.properties&quot;</span>));</span><br><span class="line">    <span class="type">String</span> <span class="variable">user</span> <span class="operator">=</span> properties.getProperty(<span class="string">&quot;user&quot;</span>);</span><br><span class="line">    <span class="type">String</span> <span class="variable">password</span> <span class="operator">=</span> properties.getProperty(<span class="string">&quot;password&quot;</span>);</span><br><span class="line">    <span class="type">String</span> <span class="variable">url</span> <span class="operator">=</span> properties.getProperty(<span class="string">&quot;url&quot;</span>);</span><br><span class="line">    <span class="type">String</span> <span class="variable">driver</span> <span class="operator">=</span> properties.getProperty(<span class="string">&quot;driver&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 3.给数据源 comboPooledDataSource 设置相关信息</span></span><br><span class="line">    <span class="comment">// 连接由 comboPooledDataSource 管理</span></span><br><span class="line">    comboPooledDataSource.setDriverClass(driver);</span><br><span class="line">    comboPooledDataSource.setUser(user);</span><br><span class="line">    comboPooledDataSource.setPassword(password);</span><br><span class="line">    comboPooledDataSource.setJdbcUrl(url);</span><br><span class="line">    <span class="comment">// 设置初始化连接数</span></span><br><span class="line">    comboPooledDataSource.setInitialPoolSize(<span class="number">10</span>);</span><br><span class="line">    <span class="comment">// 最大连接数</span></span><br><span class="line">    comboPooledDataSource.setMaxPoolSize(<span class="number">50</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 测试连接池效率</span></span><br><span class="line">    <span class="type">long</span> <span class="variable">start</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">5000</span>; i++) &#123;</span><br><span class="line">        <span class="comment">// 4.连接，从 DataSource 接口实现</span></span><br><span class="line">        <span class="type">Connection</span> <span class="variable">connection</span> <span class="operator">=</span> comboPooledDataSource.getConnection();</span><br><span class="line">        <span class="comment">// 5.关闭资源</span></span><br><span class="line">        connection.close();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">long</span> <span class="variable">end</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line">    System.out.println(<span class="string">&quot;C3P0 连接池 5000 次连接耗时=&quot;</span> + (end - start) + <span class="string">&quot;ms&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 方式2，使用配置文件方式完成(常用)</span></span><br><span class="line"><span class="comment">// 将 C3P0 提供的配置文件 c3p0-config.xml 放置 src 文件夹下</span></span><br><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testC3P0_02</span><span class="params">()</span> <span class="keyword">throws</span> Exception&#123;</span><br><span class="line">    <span class="comment">// 1.创建数据源对象，数据源名称看配置文件</span></span><br><span class="line">    <span class="type">ComboPooledDataSource</span> <span class="variable">comboPooledDataSource</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ComboPooledDataSource</span>(<span class="string">&quot;zcm&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 测试连接池效率</span></span><br><span class="line">    <span class="type">long</span> <span class="variable">start</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">5000</span>; i++) &#123;</span><br><span class="line">        <span class="comment">// 2.直接连接，从 DataSource 接口实现</span></span><br><span class="line">        <span class="type">Connection</span> <span class="variable">connection</span> <span class="operator">=</span> comboPooledDataSource.getConnection();</span><br><span class="line">        <span class="comment">// 3.关闭资源</span></span><br><span class="line">        connection.close();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">long</span> <span class="variable">end</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line">    System.out.println(<span class="string">&quot;C3P0 连接池 5000 次连接耗时=&quot;</span> + (end - start) + <span class="string">&quot;ms&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>Druid 使用演示（最常用，性能最优）</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testDruid</span><span class="params">()</span> <span class="keyword">throws</span> Exception&#123;</span><br><span class="line">    <span class="comment">// 1.创建 Properities 对象</span></span><br><span class="line">    <span class="type">Properties</span> <span class="variable">properties</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Properties</span>();</span><br><span class="line">    properties.load(<span class="keyword">new</span> <span class="title class_">FileInputStream</span>(<span class="string">&quot;src\\druid.properties&quot;</span>));</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 2.创建一个指定参数的数据库连接池，即 Druid 数据院</span></span><br><span class="line">    <span class="type">DataSource</span> <span class="variable">dataSource</span> <span class="operator">=</span> DruidDataSourceFactory.createDataSource(properties);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 测试连接池效率</span></span><br><span class="line">    <span class="type">long</span> <span class="variable">start</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">5000</span>; i++) &#123;</span><br><span class="line">        <span class="comment">// 3.连接，从 DataSource 接口实现</span></span><br><span class="line">        <span class="type">Connection</span> <span class="variable">connection</span> <span class="operator">=</span> dataSource.getConnection();</span><br><span class="line">        <span class="comment">// 4.关闭资源</span></span><br><span class="line">        connection.close();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">long</span> <span class="variable">end</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line">    System.out.println(<span class="string">&quot;Druid 连接池 5000 次连接耗时=&quot;</span> + (end - start) + <span class="string">&quot;ms&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li>
</ul>
</li>
<li><p>Apache——<a target="_blank" rel="noopener" href="https://www.jianshu.com/p/10241754cdd7">DBUtils</a>：元数据</p>
<ul>
<li><p>存在问题</p>
<ul>
<li><code>ResultSet</code> 结果集与 <code>connection</code> 是相关联的，关闭 <code>connection</code> 后，<code>ResultSet</code> 结果集失效，无法使用</li>
<li><code>ResultSet</code> 相当于只能用一次，不利于数据的管理</li>
<li>使用返回信息不方便</li>
</ul>
</li>
<li><p>解决方法：</p>
<ul>
<li>根据表创建一个对应类，列名对应属性，提供相应的 get、set 方法</li>
<li>此时一行记录对应一个对象（称为 <code>JavaBean、POJO、Domain</code> 对象），将结果集返回的结果保存到集合 <code>ArrayList&lt;对应类&gt;</code> 中</li>
</ul>
</li>
<li><p>使用 <code>DBUtils</code></p>
<ul>
<li><code>DbUtils</code> 类 ：提供如加载驱动、关闭连接、事务提交、回滚等常规工作的工具类，里面的所有方法都是静态的</li>
<li><code>QueryRunner</code> 类：简单化了SQL查询，它与 <code>ResultSetHandler</code> 组合在一起使用可以完成大部分的数据库操作，能够大大减少编码量</li>
<li><code>ResultSetHandler</code> 接口：用于处理 <code>java.sql.ResultSet</code>，将数据按要求转换为另一种形式</li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// select 查询多行多列结果</span></span><br><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testQuertMany</span><span class="params">()</span> <span class="keyword">throws</span> SQLException &#123;</span><br><span class="line">    <span class="comment">// 1.得到连接</span></span><br><span class="line">    <span class="type">Connection</span> <span class="variable">connection</span> <span class="operator">=</span> JDBCUtilsByDruid.getConnection();</span><br><span class="line">    <span class="comment">// 2.创建 QueryRunner 对象</span></span><br><span class="line">    <span class="type">QueryRunner</span> <span class="variable">queryRunner</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">QueryRunner</span>();</span><br><span class="line">    <span class="comment">// 3.执行相关操作</span></span><br><span class="line">    <span class="type">String</span> <span class="variable">sql</span> <span class="operator">=</span> <span class="string">&quot;select id, name from actor where id &gt;= ?&quot;</span>;</span><br><span class="line">    <span class="comment">// query 方法完成工作：执行 sql 语句，得到 resultSet 并封装到 list 集合中</span></span><br><span class="line">    <span class="comment">// new BeanListHandler&lt;&gt;(Actor.class): 将 resultSet 封装到 list 中时使用反射机制</span></span><br><span class="line">    <span class="comment">// 1: 传给 ？    是可变形参，可以有多个</span></span><br><span class="line">    <span class="comment">// 底层会使用到 ResultSet、PreparedStatement，均已关闭</span></span><br><span class="line">    List&lt;Actor&gt; query =</span><br><span class="line">        queryRunner.query(connection, sql, <span class="keyword">new</span> <span class="title class_">BeanListHandler</span>&lt;&gt;(Actor.class), <span class="number">1</span>);</span><br><span class="line">    <span class="keyword">for</span> (Actor actor : query) &#123;</span><br><span class="line">        System.out.println(actor);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 释放资源</span></span><br><span class="line">    JDBCUtilsByDruid.close(<span class="literal">null</span>, <span class="literal">null</span>, connection);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// select 查询单行多列结果</span></span><br><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testQuertSingle</span><span class="params">()</span> <span class="keyword">throws</span> SQLException &#123;</span><br><span class="line">    <span class="comment">// 1.得到连接</span></span><br><span class="line">    <span class="type">Connection</span> <span class="variable">connection</span> <span class="operator">=</span> JDBCUtilsByDruid.getConnection();</span><br><span class="line">    <span class="comment">// 2.创建 QueryRunner 对象</span></span><br><span class="line">    <span class="type">QueryRunner</span> <span class="variable">queryRunner</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">QueryRunner</span>();</span><br><span class="line">    <span class="comment">// 3.执行相关操作</span></span><br><span class="line">    <span class="type">String</span> <span class="variable">sql</span> <span class="operator">=</span> <span class="string">&quot;select * from actor where id = ?&quot;</span>;</span><br><span class="line">    <span class="comment">// 与多行多列不同，传入 BeanHandler，而不是 BeanListHandler</span></span><br><span class="line">    <span class="type">Actor</span> <span class="variable">query</span> <span class="operator">=</span> queryRunner.query(connection, sql, <span class="keyword">new</span> <span class="title class_">BeanHandler</span>&lt;&gt;(Actor.class), <span class="number">1</span>);</span><br><span class="line">    System.out.println(query);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 释放资源</span></span><br><span class="line">    JDBCUtilsByDruid.close(<span class="literal">null</span>, <span class="literal">null</span>, connection);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// select 查询单行单列结果</span></span><br><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testQuertScalar</span><span class="params">()</span> <span class="keyword">throws</span> SQLException &#123;</span><br><span class="line">    <span class="comment">// 1.得到连接</span></span><br><span class="line">    <span class="type">Connection</span> <span class="variable">connection</span> <span class="operator">=</span> JDBCUtilsByDruid.getConnection();</span><br><span class="line">    <span class="comment">// 2.创建 QueryRunner 对象</span></span><br><span class="line">    <span class="type">QueryRunner</span> <span class="variable">queryRunner</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">QueryRunner</span>();</span><br><span class="line">    <span class="comment">// 3.执行相关操作</span></span><br><span class="line">    <span class="type">String</span> <span class="variable">sql</span> <span class="operator">=</span> <span class="string">&quot;select name from actor where id = ?&quot;</span>;</span><br><span class="line">    <span class="comment">// 单行单列，返回一个对象，使用 ScalarHandler</span></span><br><span class="line">    <span class="type">Object</span> <span class="variable">obj</span> <span class="operator">=</span> queryRunner.query(connection, sql, <span class="keyword">new</span> <span class="title class_">ScalarHandler</span>(), <span class="number">1</span>);</span><br><span class="line">    System.out.println(obj);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 释放资源</span></span><br><span class="line">    JDBCUtilsByDruid.close(<span class="literal">null</span>, <span class="literal">null</span>, connection);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// DML</span></span><br><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testDML</span><span class="params">()</span> <span class="keyword">throws</span> SQLException &#123;</span><br><span class="line">    <span class="comment">// 1.得到连接</span></span><br><span class="line">    <span class="type">Connection</span> <span class="variable">connection</span> <span class="operator">=</span> JDBCUtilsByDruid.getConnection();</span><br><span class="line">    <span class="comment">// 2.创建 QueryRunner 对象</span></span><br><span class="line">    <span class="type">QueryRunner</span> <span class="variable">queryRunner</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">QueryRunner</span>();</span><br><span class="line">    <span class="comment">// 3.执行相关操作</span></span><br><span class="line">    <span class="comment">//        String sql = &quot;update actor set name = ? where id = ?&quot;;</span></span><br><span class="line">    <span class="comment">//        String sql = &quot;insert into actor values(null, ?, ?, ?, ?)&quot;;</span></span><br><span class="line">    <span class="type">String</span> <span class="variable">sql</span> <span class="operator">=</span> <span class="string">&quot;delete from actor where id = ?&quot;</span>;</span><br><span class="line">    <span class="comment">// 增删改都用 update 方法</span></span><br><span class="line">    <span class="comment">// 返回值为受影响的行数</span></span><br><span class="line">    <span class="type">int</span> <span class="variable">affectedRowCount</span> <span class="operator">=</span> queryRunner.update(connection, sql, <span class="number">2</span>);</span><br><span class="line">    System.out.println(affectedRowCount &gt; <span class="number">0</span> ? <span class="string">&quot;执行成功&quot;</span> : <span class="string">&quot;无影响&quot;</span>);</span><br><span class="line">    <span class="comment">// 释放资源</span></span><br><span class="line">    JDBCUtilsByDruid.close(<span class="literal">null</span>, <span class="literal">null</span>, connection);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li>
</ul>
</li>
<li><p><a target="_blank" rel="noopener" href="https://www.bilibili.com/video/BV1fh411y7R8?p=853"><code>BasicDao</code></a> ：增删改查通用方法 </p>
<ul>
<li><p><code>DAO</code>：<code>data access object</code> 即访问数据的对象</p>
</li>
<li><p>传统方法不足：</p>
<ul>
<li>SQL 语句是固定的，不能通过参数传入，通用性差</li>
<li>查询操作的返回值类型固定，应使用泛型</li>
<li>业务需求复杂时，不能只靠一个 Java 类完成</li>
</ul>
</li>
<li><p><code>XXXDao</code> 处理对应 <code>XXX类（对应数据库中的XXX表）</code>，其中的公共方法封装为 <code>BasicDao</code>，当作父类去继承</p>
<p><a target="_blank" rel="noopener" href="https://imgtu.com/i/HN1KjP"><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/%E9%9B%B6%E5%9F%BA%E7%A1%80%E5%AD%A6Java/HN1KjP.png" alt="HN1KjP.png"></a></p>
</li>
<li><p><code>BasicDao</code> 实现</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// BasicDao 实现</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BasicDAO</span>&lt;T&gt; &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">QueryRunner</span> <span class="variable">queryRunner</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">QueryRunner</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 通用 dml 方法，针对任意表</span></span><br><span class="line">    <span class="comment">// parameters 表示代替 sql 中 ？ 的参数值</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">update</span><span class="params">(String sql, Object... parameters)</span> &#123;</span><br><span class="line">        <span class="type">Connection</span> <span class="variable">connection</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            connection = JDBCUtilsByDruid.getConnection();</span><br><span class="line">            <span class="type">int</span> <span class="variable">update</span> <span class="operator">=</span> queryRunner.update(connection, sql, parameters);</span><br><span class="line">            <span class="keyword">return</span> update;</span><br><span class="line">        &#125; <span class="keyword">catch</span> (SQLException e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e);</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            JDBCUtilsByDruid.close(<span class="literal">null</span>, <span class="literal">null</span>, connection);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 查询多行多列，针对任意表   T 表泛型</span></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> sql           sql 语句，可以有 ?</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> tClass        传入一个类的 Class 对象，如 Actor.class</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> parameters    传入 ? 的具体的值，可以是多个</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span>              根据 Actor.class，返回对应的 ArrayList 集合</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> List&lt;T&gt; <span class="title function_">queryMulti</span><span class="params">(String sql, Class&lt;T&gt; tClass,  Object... parameters)</span> &#123;</span><br><span class="line">        <span class="type">Connection</span> <span class="variable">connection</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            connection = JDBCUtilsByDruid.getConnection();</span><br><span class="line">            <span class="keyword">return</span> queryRunner.query(connection, sql, <span class="keyword">new</span> <span class="title class_">BeanListHandler</span>&lt;T&gt;(tClass), parameters);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (SQLException e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e);</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            JDBCUtilsByDruid.close(<span class="literal">null</span>, <span class="literal">null</span>, connection);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 查询单行多列，针对任意表   T 表泛型</span></span><br><span class="line">    <span class="keyword">public</span> T <span class="title function_">querySingle</span><span class="params">(String sql, Class&lt;T&gt; tClass,  Object... parameters)</span> &#123;</span><br><span class="line">        <span class="type">Connection</span> <span class="variable">connection</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            connection = JDBCUtilsByDruid.getConnection();</span><br><span class="line">            <span class="keyword">return</span> queryRunner.query(connection, sql, <span class="keyword">new</span> <span class="title class_">BeanHandler</span>&lt;T&gt;(tClass), parameters);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (SQLException e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e);</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            JDBCUtilsByDruid.close(<span class="literal">null</span>, <span class="literal">null</span>, connection);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 查询单行单列，针对任意表   T 表泛型</span></span><br><span class="line">    <span class="keyword">public</span> Object <span class="title function_">queryScalar</span><span class="params">(String sql, Object... parameters)</span> &#123;</span><br><span class="line">        <span class="type">Connection</span> <span class="variable">connection</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            connection = JDBCUtilsByDruid.getConnection();</span><br><span class="line">            <span class="keyword">return</span> queryRunner.query(connection, sql, <span class="keyword">new</span> <span class="title class_">ScalarHandler</span>(), parameters);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (SQLException e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e);</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            JDBCUtilsByDruid.close(<span class="literal">null</span>, <span class="literal">null</span>, connection);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ActorDAO 实现</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ActorDAO</span> <span class="keyword">extends</span> <span class="title class_">BasicDAO</span>&lt;Actor&gt; &#123;</span><br><span class="line">	<span class="comment">// 针对 Actor 表的特殊操作</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 测试 ActorDAO</span></span><br><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testActorDAO</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="type">ActorDAO</span> <span class="variable">actorDAO</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ActorDAO</span>();</span><br><span class="line">    <span class="type">String</span> <span class="variable">sql</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="comment">// 1.查询:多行多列</span></span><br><span class="line">    sql = <span class="string">&quot;select * from actor where id &gt;= ?&quot;</span>;</span><br><span class="line">    List&lt;Actor&gt; actors = actorDAO.queryMulti(sql, Actor.class, <span class="number">1</span>);</span><br><span class="line">    <span class="keyword">for</span> (Actor actor : actors) &#123;</span><br><span class="line">        System.out.println(actor);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 2.查询:单行多列</span></span><br><span class="line">    sql = <span class="string">&quot;select * from actor where id = ?&quot;</span>;</span><br><span class="line">    <span class="type">Actor</span> <span class="variable">actor</span> <span class="operator">=</span> actorDAO.querySingle(sql, Actor.class, <span class="number">3</span>);</span><br><span class="line">    System.out.println(actor);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 3.查询:单行单列</span></span><br><span class="line">    sql = <span class="string">&quot;select name from actor where id = ?&quot;</span>;</span><br><span class="line">    <span class="type">Object</span> <span class="variable">obj</span> <span class="operator">=</span> actorDAO.queryScalar(sql, <span class="number">2</span>);</span><br><span class="line">    System.out.println(obj);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 4.dml:插入</span></span><br><span class="line">    sql = <span class="string">&quot;insert into actor values(null, ?, ?, ?, ?)&quot;</span>;</span><br><span class="line">    <span class="type">int</span> <span class="variable">update</span> <span class="operator">=</span> actorDAO.update(sql, <span class="string">&quot;张无忌&quot;</span>, <span class="string">&quot;女&quot;</span>, <span class="string">&quot;2020-6-8&quot;</span>, <span class="string">&quot;778&quot;</span>);</span><br><span class="line">    System.out.println(update &gt; <span class="number">0</span> ? <span class="string">&quot;执行成功&quot;</span> : <span class="string">&quot;无影响&quot;</span>);</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li>
</ul>
</li>
</ul>
<h3 id="正则表达式-Regular-Expression（RegExp）"><a href="#正则表达式-Regular-Expression（RegExp）" class="headerlink" title="正则表达式-Regular Expression（RegExp）"></a><a target="_blank" rel="noopener" href="https://www.runoob.com/regexp/regexp-tutorial.html">正则表达式-Regular Expression（RegExp）</a></h3><ul>
<li><p><strong>学会在 Java 中的使用步骤与底层实现即可，细节用到再看，边用边学</strong></p>
</li>
<li><p>正则查找步骤</p>
<ul>
<li>写出正则表达式：<code>String regStr = &quot;...&quot;</code></li>
<li>创建模式对象：<code>Pattern pattern = Pattern.compile(regStr)</code></li>
<li>创建匹配器：<code>Matcher matcher = pattern.matcher(待查找文本)</code></li>
<li>完成匹配：<code>while(matcher.find()) &#123; System.out.println(matcher.group(0)); &#125;</code></li>
</ul>
</li>
<li><p><a target="_blank" rel="noopener" href="https://www.bilibili.com/video/BV1fh411y7R8?p=881">正则表达式的底层实现</a><strong>（不考虑分组）</strong></p>
<ul>
<li><p><code>matcher.find()</code></p>
<ul>
<li><p>根据指定的规则，定位满足规则的子字符串</p>
</li>
<li><p>找到后，将子字符串的开始的索引记录到 <code>matcher</code> 对象的属性 <code>int[] groups</code></p>
<p>第一个匹配的子串：<code>groups[0] = 开始索引</code>      <code>groups[1] = 结束索引 + 1</code></p>
</li>
<li><p>同时记录 <code>oldLast</code> 的值为 <code>子字符串的结束的索引 + 1</code> 的值，下次执行 <code>find()</code> 时，就从 <code>oldLast</code> 开始匹配</p>
</li>
</ul>
</li>
<li><p><code>matcher.group(0)</code></p>
<p>根据 <code>groups[0]</code> 和 <code>groups[]</code> 的记录的位置，从 content 截取子字符串返回 </p>
<p>左闭右开：<code>[ g[0], g[1] )</code></p>
</li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">while</span>(matcher.find()) &#123;</span><br><span class="line">    System.out.println(matcher.group(<span class="number">0</span>));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p><a target="_blank" rel="noopener" href="https://www.bilibili.com/video/BV1fh411y7R8?p=882">正则表达式的底层实现</a><strong>（考虑分组）</strong></p>
<ul>
<li><p><code>matcher.find()</code></p>
<ul>
<li><p>根据指定的规则，定位满足规则的子字符串</p>
</li>
<li><p>找到后，将子字符串的开始的索引记录到 <code>matcher</code> 对象的属性 <code>int[] groups</code></p>
<p>第一个匹配的子串：<code>groups[0] = 开始索引</code>      <code>groups[1] = 结束索引 + 1</code></p>
<p>子串中匹配到第一分组的子字串：<code>groups[2] = 开始索引</code>      <code>groups[3] = 结束索引 + 1</code></p>
<p>子串中匹配到第二分组的子字串：<code>groups[4] = 开始索引</code>      <code>groups[5] = 结束索引 + 1</code></p>
<p>以此类推。。。</p>
</li>
<li><p>同时记录 <code>oldLast</code> 的值为 <code>groups[1] + 1</code> 的值，下次执行 <code>find()</code> 时，就从 <code>oldLast</code> 开始匹配</p>
</li>
</ul>
</li>
<li><p><code>matcher.group(i)</code></p>
<p>根据 <code>groups[i * 2]</code> 和 <code>groups[i * 2 + 1]</code> 的记录的位置，从 content 截取子字符串返回 </p>
<p>左闭右开：<code>[ g[i * 2], g[i * 2 + 1] )</code></p>
</li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">while</span>(matcher.find()) &#123;</span><br><span class="line">    <span class="comment">// 1.如果正则表达式有() 即分组</span></span><br><span class="line">    <span class="comment">// 2.取出匹配的字符串规则如下</span></span><br><span class="line">    <span class="comment">// 3.group(0) 表示匹配到的子字符串</span></span><br><span class="line">    <span class="comment">// 4.group(1) 表示匹配到的子字符串的第1组字串</span></span><br><span class="line">    <span class="comment">// 5.group(2) 表示匹配到的子字符串的第2组字串</span></span><br><span class="line">    <span class="comment">// 6.以此类推... 但是分组的数不能越界</span></span><br><span class="line">    System.out.println(<span class="string">&quot;整体: &quot;</span> + matcher.group(<span class="number">0</span>));</span><br><span class="line">    System.out.println(<span class="string">&quot;第1组()匹配到的值=&quot;</span> + matcher.group(<span class="number">1</span>));</span><br><span class="line">    System.out.println(<span class="string">&quot;第2组()匹配到的值=&quot;</span> + matcher.group(<span class="number">2</span>));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>元字符(Metacharacter) - 转义符：<code>\\</code></p>
<ul>
<li>使用正则表达式匹配特殊字符，如 <code>* + ( ) $ / \ ? [ ] ^ &#123; &#125;</code> 时，需用到转义符</li>
<li>Java 的正则表达式中 <code>\\</code> 表示转义符，其它语言中一般只用 <code>\</code> 表示转义符</li>
</ul>
</li>
<li><p><a target="_blank" rel="noopener" href="https://www.runoob.com/regexp/regexp-metachar.html">元字符 - 限定符 &amp; 定位符</a></p>
</li>
<li><p>修饰符</p>
<p><a target="_blank" rel="noopener" href="https://imgtu.com/i/HaU81S"><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/%E9%9B%B6%E5%9F%BA%E7%A1%80%E5%AD%A6Java/HaU81S.png" alt="HaU81S.png"></a></p>
</li>
<li><p>运算符优先级</p>
<ul>
<li><p>正则表达式从左到右进行计算，并遵循优先级顺序</p>
</li>
<li><p>相同优先级的从左到右进行运算，不同优先级的运算先高后低。下表从最高到最低说明了各种正则表达式运算符的优先级顺序</p>
<p><a target="_blank" rel="noopener" href="https://imgtu.com/i/Haapjg"><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/%E9%9B%B6%E5%9F%BA%E7%A1%80%E5%AD%A6Java/Haapjg.png" alt="Haapjg.png"></a></p>
</li>
</ul>
</li>
<li><p><a target="_blank" rel="noopener" href="https://c.runoob.com/front-end/854/">常用正则表达式</a></p>
<ul>
<li>链接为正则表达式在线测试</li>
<li>最下面有常用正则表达式</li>
</ul>
</li>
<li><p>正则表达式三个常用类</p>
<ul>
<li><p><code>Pattern</code> 类</p>
<ul>
<li><p><code>Pattern</code> 对象是一个正则表达式对象</p>
</li>
<li><p>没有公共构造方法，创建对象时调用公共静态方法，其参数为正则表达式</p>
<p>如 <code>Pattern pattern = Pattern.compile(regStr)</code></p>
</li>
<li><p>整体匹配方法：<code>Pattern.matcher(regStr,content)</code>  </p>
<p>检查 <code>content</code> 整体是否满足正则表达式 <code>regStr</code></p>
</li>
</ul>
</li>
<li><p><code>Matcher</code> 类</p>
<ul>
<li><p><code>Matcher</code> 对象是对输入字符串进行解释和匹配的引擎</p>
</li>
<li><p>没有公共构造方法，创建对象时通过 <code>Pattern</code> 调用</p>
<p>如 <code>Matcher matcher = pattern.matcher(content)</code>；其中 <code>content</code> 为待匹配的内容</p>
</li>
<li><p>整体匹配方法：<code>matcher.matches()</code>  </p>
</li>
<li><p>替换方法：<code>String newContent = matcher.replaceAll(&quot;替换成的内容&quot;)</code></p>
<p>根据正则匹配替换全部匹配到的子串，返回的字符串才是替换后的字符串，原 content 不变</p>
</li>
</ul>
</li>
<li><p><code>PatternSyntaxException</code> 类</p>
<ul>
<li><code>PatternSyntaxException</code> 是个非强制异常类，表示正则表达式模式中的语法错误</li>
</ul>
</li>
</ul>
</li>
<li><p>分组、捕获、反向引用</p>
<ul>
<li><p><strong>分组</strong>：用圆括号组成一个比较复杂的匹配模式，圆括号的部分看作是<strong>子表达式／分组</strong></p>
</li>
<li><p><strong>捕获</strong>：把正则表达式中<strong>子表达式／分组</strong>匹配的内容，保存到内存中以数字编号或显式命名的组里，方便后面引用</p>
<p>从左向右，以分组的左括号为标志，第一个出现的分组的组号为 1，第二个为 2，以此类推</p>
<p>组 0 代表的是整个正则式</p>
</li>
<li><p><strong>反向引用</strong>：圆括号的内容被捕获后，可以在这个括号后被使用，从而写出一个比较实用的匹配模式</p>
<p>这种引用既可以是在正则表达式内部，也可以是在正则表达式外部</p>
<p>内部反向引用 <code>\\ 分组号</code>，外部反向引用<code>＄分组号</code></p>
</li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 演示反向引用</span></span><br><span class="line"></span><br><span class="line"><span class="type">String</span> <span class="variable">content</span> <span class="operator">=</span> <span class="string">&quot;h1234el9876lo33333 j12324-333999111a1551ck14 tom11 jack22 yyy12345 xxx&quot;</span>;</span><br><span class="line"><span class="comment">// 要匹配两个连续的相同数字</span></span><br><span class="line"><span class="comment">// String regStr = &quot;(\\d)\\1&quot;;</span></span><br><span class="line"><span class="comment">// 要匹配五个连续的相同数字</span></span><br><span class="line"><span class="comment">// String regStr = &quot;(\\d)\\1&#123;4&#125;&quot;;</span></span><br><span class="line"><span class="comment">// 要匹配个位与千位相同，十位与百位相同的数，如 5225, 1551</span></span><br><span class="line"><span class="comment">// String regStr = &quot;(\\d)(\\d)\\2\\1&quot;;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">* 请在字符串中检索商品编号,形式如:12321-333999111 这样的号码,</span></span><br><span class="line"><span class="comment">* 要求满足前面是一个五位数,然后一个-号,然后是一个九位数,连续的每三位要相同</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="type">String</span> <span class="variable">regStr</span> <span class="operator">=</span> <span class="string">&quot;\\d&#123;5&#125;-(\\d)\\1&#123;2&#125;(\\d)\\2&#123;2&#125;(\\d)\\3&#123;2&#125;&quot;</span>;</span><br><span class="line"><span class="type">Pattern</span> <span class="variable">pattern</span> <span class="operator">=</span> Pattern.compile(regStr);</span><br><span class="line"><span class="type">Matcher</span> <span class="variable">matcher</span> <span class="operator">=</span> pattern.matcher(content);</span><br><span class="line"><span class="keyword">while</span> (matcher.find()) &#123;</span><br><span class="line">    System.out.println(<span class="string">&quot;找到 &quot;</span> + matcher.group(<span class="number">0</span>));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 经典结巴案例</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">    <span class="type">String</span> <span class="variable">content</span> <span class="operator">=</span> <span class="string">&quot;我....我要....学学学学....编程java!&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 1.去掉所有的.</span></span><br><span class="line">    <span class="type">Pattern</span> <span class="variable">pattern</span> <span class="operator">=</span> Pattern.compile(<span class="string">&quot;\\.&quot;</span>);</span><br><span class="line">    <span class="type">Matcher</span> <span class="variable">matcher</span> <span class="operator">=</span> pattern.matcher(content);</span><br><span class="line">    content = matcher.replaceAll(<span class="string">&quot;&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 2.去掉重复的字  我我要学学学学编程java!</span></span><br><span class="line">    <span class="comment">// (1) 内部反向引用找到需替换的内同 (.)\\1+</span></span><br><span class="line">    <span class="comment">// 注意：因为正则表达式变化，所以需要重置 matcher</span></span><br><span class="line">    <span class="comment">// 分组的内容记录到 matcher 的 $1 中</span></span><br><span class="line">    pattern = Pattern.compile(<span class="string">&quot;(.)\\1+&quot;</span>);</span><br><span class="line">    matcher = pattern.matcher(content);</span><br><span class="line">    <span class="keyword">while</span> (matcher.find()) &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;找到=&quot;</span> + matcher.group(<span class="number">0</span>));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// (2) 外部使用反向引用 $1 来替换(1)</span></span><br><span class="line">    content = matcher.replaceAll(<span class="string">&quot;$1&quot;</span>);</span><br><span class="line">    System.out.println(<span class="string">&quot;content=&quot;</span> + content);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 2.一行代码去掉重复的字  我我要学学学学编程java!</span></span><br><span class="line">    <span class="comment">//        content = Pattern.compile(&quot;(.)\\1+&quot;).matcher(content).replaceAll(&quot;$1&quot;);</span></span><br><span class="line">    <span class="comment">//        System.out.println(&quot;content=&quot; + content);</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>String 操作中使用正则表达式</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">String 类:</span><br><span class="line"><span class="keyword">public</span> String <span class="title function_">replaceAll</span><span class="params">(String regex,String replacement)</span>;</span><br><span class="line"><span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">matches</span><span class="params">(String regex)</span>;</span><br><span class="line"><span class="keyword">public</span> String[] split(String regex)</span><br></pre></td></tr></table></figure></li>
</ul>
</article><div class="post-copyright"><div class="post-copyright__author"><span class="post-copyright-meta">文章作者: </span><span class="post-copyright-info"><a href="https://gitee.com/zcmmmm/zcmmmm">zcm</a></span></div><div class="post-copyright__type"><span class="post-copyright-meta">文章链接: </span><span class="post-copyright-info"><a href="https://gitee.com/zcmmmm/zcmmmm/2022/09/23/%E9%9B%B6%E5%9F%BA%E7%A1%80%E5%AD%A6-Java-03/">https://gitee.com/zcmmmm/zcmmmm/2022/09/23/%E9%9B%B6%E5%9F%BA%E7%A1%80%E5%AD%A6-Java-03/</a></span></div><div class="post-copyright__notice"><span class="post-copyright-meta">版权声明: </span><span class="post-copyright-info">本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">CC BY-NC-SA 4.0</a> 许可协议。转载请注明来自 <a href="https://gitee.com/zcmmmm/zcmmmm" target="_blank">无人深空</a>！</span></div></div><div class="tag_share"><div class="post-meta__tag-list"><a class="post-meta__tags" href="/tags/Java/">Java</a></div><div class="post_share"><div class="social-share" data-image="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/%E9%9B%B6%E5%9F%BA%E7%A1%80%E5%AD%A6Java/Cover.jpg" data-sites="facebook,twitter,wechat,weibo,qq"></div><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/butterfly-extsrc/sharejs/dist/css/share.min.css" media="print" onload="this.media='all'"><script src="https://cdn.jsdelivr.net/npm/butterfly-extsrc/sharejs/dist/js/social-share.min.js" defer></script></div></div><nav class="pagination-post" id="pagination"><div class="prev-post pull-left"><a href="/2022/09/23/Java-Web/"><img class="prev-cover" src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JavaWeb/Java%20Web.jpg" onerror="onerror=null;src='/img/404.jpg'" alt="cover of previous post"><div class="pagination-info"><div class="label">上一篇</div><div class="prev_info">JavaWeb</div></div></a></div><div class="next-post pull-right"><a href="/2022/09/23/%E9%9B%B6%E5%9F%BA%E7%A1%80%E5%AD%A6-Java-02/"><img class="next-cover" src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/%E9%9B%B6%E5%9F%BA%E7%A1%80%E5%AD%A6Java/Cover.jpg" onerror="onerror=null;src='/img/404.jpg'" alt="cover of next post"><div class="pagination-info"><div class="label">下一篇</div><div class="next_info">零基础学-Java-02</div></div></a></div></nav><div class="relatedPosts"><div class="headline"><i class="fas fa-thumbs-up fa-fw"></i><span>相关推荐</span></div><div class="relatedPosts-list"><div><a href="/2023/03/03/Java%E5%87%BD%E6%95%B0%E5%BC%8F%E7%BC%96%E7%A8%8B/" title="Java函数式编程"><img class="cover" src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/FunctionalProgramming/Functional%20programmi.png" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2023-03-03</div><div class="title">Java函数式编程</div></div></a></div><div><a href="/2023/04/17/Java%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86%E7%82%B9%E6%96%87%E7%AB%A0%E6%B1%87%E6%80%BB/" title="Java 基础知识点文章汇总"><img class="cover" src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java.png" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2023-04-17</div><div class="title">Java 基础知识点文章汇总</div></div></a></div><div><a href="/2022/09/23/%E9%9B%B6%E5%9F%BA%E7%A1%80%E5%AD%A6-Java-01/" title="零基础学-Java-01"><img class="cover" src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/%E9%9B%B6%E5%9F%BA%E7%A1%80%E5%AD%A6Java/Cover.jpg" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2022-09-23</div><div class="title">零基础学-Java-01</div></div></a></div><div><a href="/2022/09/23/%E9%9B%B6%E5%9F%BA%E7%A1%80%E5%AD%A6-Java-02/" title="零基础学-Java-02"><img class="cover" src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/%E9%9B%B6%E5%9F%BA%E7%A1%80%E5%AD%A6Java/Cover.jpg" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2022-09-23</div><div class="title">零基础学-Java-02</div></div></a></div></div></div></div><div class="aside-content" id="aside-content"><div class="sticky_layout"><div class="card-widget" id="card-toc"><div class="item-headline"><i class="fas fa-stream"></i><span>目录</span><span class="toc-percentage"></span></div><div class="toc-content is-expand"><ol class="toc"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B"><span class="toc-text">网络编程</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%8F%8D%E5%B0%84%E6%9C%BA%E5%88%B6"><span class="toc-text">反射机制</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%95%B0%E6%8D%AE%E5%BA%93-MySQL"><span class="toc-text">数据库 MySQL</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#JDBC%E5%92%8C%E6%95%B0%E6%8D%AE%E5%BA%93%E8%BF%9E%E6%8E%A5%E6%B1%A0"><span class="toc-text">JDBC和数据库连接池</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F-Regular-Expression%EF%BC%88RegExp%EF%BC%89"><span class="toc-text">正则表达式-Regular Expression（RegExp）</span></a></li></ol></div></div></div></div></main><footer id="footer"><div id="footer-wrap"><div class="copyright">&copy;2022 - 2023 By zcm</div><div class="framework-info"><span>框架 </span><a target="_blank" rel="noopener" href="https://hexo.io">Hexo</a><span class="footer-separator">|</span><span>主题 </span><a target="_blank" rel="noopener" href="https://github.com/jerryc127/hexo-theme-butterfly">Butterfly</a></div></div></footer></div><div id="rightside"><div id="rightside-config-hide"><button id="readmode" type="button" title="阅读模式"><i class="fas fa-book-open"></i></button><button id="darkmode" type="button" title="浅色和深色模式转换"><i class="fas fa-adjust"></i></button><button id="hide-aside-btn" type="button" title="单栏和双栏切换"><i class="fas fa-arrows-alt-h"></i></button></div><div id="rightside-config-show"><button id="rightside_config" type="button" title="设置"><i class="fas fa-cog fa-spin"></i></button><button class="close" id="mobile-toc-button" type="button" title="目录"><i class="fas fa-list-ul"></i></button><button id="go-up" type="button" title="回到顶部"><i class="fas fa-arrow-up"></i></button></div></div><div id="local-search"><div class="search-dialog"><nav class="search-nav"><span class="search-dialog-title">搜索</span><span id="loading-status"></span><button class="search-close-button"><i class="fas fa-times"></i></button></nav><div class="is-center" id="loading-database"><i class="fas fa-spinner fa-pulse"></i><span>  数据库加载中</span></div><div class="search-wrap"><div id="local-search-input"><div class="local-search-box"><input class="local-search-box--input" placeholder="搜索文章" type="text"/></div></div><hr/><div id="local-search-results"></div></div></div><div id="search-mask"></div></div><div><script src="/js/utils.js"></script><script src="/js/main.js"></script><script src="https://cdn.jsdelivr.net/npm/@fancyapps/ui/dist/fancybox.umd.min.js"></script><script src="https://cdn.jsdelivr.net/npm/node-snackbar/dist/snackbar.min.js"></script><script src="/js/search/local-search.js"></script><div class="js-pjax"></div><script src="/js/categories.js?v1"></script><script src="/js/navigation.js?v1"></script><script async data-pjax src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script></div></body></html>